Diffuse Lighting (Diffuse reflection light)Different material surfaces reflect light in different ways. The reflection angle of the mirror glazing is equal to the angle of the entering body. When you see a strange light in the eyes of a cat, this is the reflection of light: this is because the eyes of the cat are reflected in the direction of the light and the direction of the light, but in the opposite direction. The diffuse reflection surface is the same in all directions.
Approximate calculation of a diffuse light, the simplest and most commonly used model is Lambert's cosine law (Lambert cosine). According to Lambert's cosine law, the illumination intensity of the material surface is proportional to the cosine of the angle of the light source direction vector and polygon normal. The light source vector describes the direction of light exposure, and the normal vector determines the orientation of the surface. Figure 6.3 illustrates these terms.
Figure 6.3 A illustration of a surface normal, a light vector, and Lambert's cosine law.
Recall the second chapter, "A 3d/math Primer", the vector operation discussed, through the dot product of the vector can get the cosine of the angle between the light source vector and the normal vector (both vectors are the unit vectors). In general, the normal vector is provided by each vertex when the 3D object is loaded (or computed by the cross-product of the triangle two edges).
directional Lights
Three commonly used light sources are defined in 3D graphics: directional lights,point lights,spotlights (directional light, point Light, Spotlight). A directional light represents the source of infinity from the 3D objects, meaning that there is no coordinate position relative to the objects. As a result, the light that shines on the objects is parallel and comes in the same direction. A good example of directional light is the sunlight (though the sun is not infinitely far from the strict sense). Figure 6.4 depicts the concept of direction light.
Figure 6.4 A illustration of a directional light.
To simulate a directional light, simply use a three-dimensional vector to define the direction of the beam. It can also be like ambient light, including the color and intensity of the lights. Listing 6.2 lists a code for diffuse lighting effect, which uses a simple directional light. This book is very limited, it is recommended to copy the code into the nvidia FX composer, step by step test the lighting effect. (You can also download the code from the companion website of this book)
List 6.2 diffuselighting.fx
#include "include\\common.fxh"/************* Resources *************/cbuffer cbufferperframe {float4 Ambientcolor:
AMBIENT < string uiname = "AMBIENT light";
String uiwidget = "Color";
> = {1.0f, 1.0f, 1.0f, 0.0f};
FLOAT4 Lightcolor:color < string Object = "LightColor0";
String uiname = "Light Color";
String uiwidget = "Color";
> = {1.0f, 1.0f, 1.0f, 1.0f};
FLOAT3 Lightdirection:direction < string Object = "DirectionalLight0";
String uiname = "Light Direction";
String Space = "World";
> = {0.0f, 0.0f, -1.0f};
} cbuffer cbufferperobject {float4x4 worldviewprojection:worldviewprojection < string uiwidget= "None"; >; float4x4 World:world < string uiwidget= "None";
>;
} texture2d colortexture < string resourcename = "Default_color.dds";
String uiname = "Color Texture";
String resourcetype = "2D";
>;
Samplerstate Colorsampler {Filter = min_mag_mip_linear;
Addressu = WRAP;
ADDRESSV = WRAP;
}; RAsterizerstate disableculling {cullmode = NONE;};
/************* Data Structures *************/struct Vs_input {float4 objectposition:position;
FLOAT2 Texturecoordinate:texcoord;
FLOAT3 Normal:normal;
};
struct Vs_output {float4 position:sv_position;
FLOAT3 Normal:normal;
FLOAT2 texturecoordinate:texcoord0;
FLOAT3 Lightdirection:texcoord1;
};
/************* Vertex Shader *************/vs_output vertex_shader (vs_input in) {vs_output out = (vs_output) 0; Out. Position = Mul (in.
Objectposition, worldviewprojection); Out. Texturecoordinate = Get_corrected_texture_coordinate (in.
Texturecoordinate); Out. Normal = Normalize (Mul (FLOAT4 (in.
Normal, 0), world). xyz); Out.
Lightdirection = normalize (-lightdirection);
return out;
}/************* Pixel Shader *************/float4 pixel_shader (vs_output in): sv_target {float4 out = (float4) 0; FLOAT3 normal = normalize (in.
Normal); FLOAT3 lightdirection = Normalize (in. LightdirectiON);
float n_dot_l = dot (lightdirection, normal); FLOAT4 color = colortexture.sample (Colorsampler, in.
Texturecoordinate);
FLOAT3 ambient = Ambientcolor.rgb * AMBIENTCOLOR.A * COLOR.RGB;
FLOAT3 diffuse = (FLOAT3) 0;
if (n_dot_l > 0) {diffuse = Lightcolor.rgb * LIGHTCOLOR.A * n_dot_l * COLOR.RGB;
} Out.rgb = ambient + diffuse;
OUT.A = COLOR.A;
return out; }/************* Techniques *************/technique10 Main10 {pass P0 {SetVertexShader (Compileshader (vs_
4_0, Vertex_shader ()));
Setgeometryshader (NULL);
SetPixelShader (Compileshader (Ps_4_0, Pixel_shader ()));
Setrasterizerstate (disableculling);
}
}
Diffuse Lighting Effect Preamble
The first line of the DIFFUSELIGHTING.FX code uses a C-style # include method that contains a header file that provides some common functions that are used in the increasingly rich effects. Listing 6.3 Lists the contents of the header file Common.fxh, the header file defender with the _COMMON_FXH macro definition in the header file, and the flip_texture_y macros and Get_corrected_texture_ transferred from the previous code. The coordinate () function.
List 6.3 Common.fxh
#ifndef _common_fxh
#define _COMMON_FXH
/************* Constants *************/
#define FLIP_TEXTURE_Y 1
/************* Utility Functions *************/
float2 get_corrected_texture_coordinate (float2 Texturecoordinate)
{
#if flip_texture_y
return float2 (texturecoordinate.x, 1.0-texturecoordinate.y);
#else
return texturecoordinate;
#endif
}
float3 get_vector_color_contribution (float4 light, float3 color)
{
//color (. RGB) * Intensity (. A)
return LIGHT.RGB * light.a * color;
}
FLOAT3 Get_scalar_color_contribution (float4 light, float color)
{
//color (. RGB) * Intensity (. a)
return LIGHT.RGB * light.a * color;
}
#endif/* _COMMON_FXH */
Another thing to note is the two new Cbufferperframe members: Lightcolor and Lightdirection. Lightcolor constants have the same function as Ambientcolor: Used to represent the color and intensity of directional light. The lightdirection stores the direction of the light source in world space. These two new shader constants have corresponding object annotations, which means that the variable can be bound to a oject in the scene in the Nvidia FX composer. Specifically, you can place the light source in the render panel of the Nvidia FX composer and associate these lights with the shader constants with the object annotation.
Finally, it is important to note that the world variable is added to the cbufferperobject. The value of the variable is related to the newly added normal member variable in the Vs_input struct. As with the vertices of object, the normals of polygons are stored in object space. The diffuse color of the pixel is calculated by dot-product the normal vector to the direction vector of the light, but because the direction of the light source is in world space, the normal vector is also transformed into Wrold space, and the world matrix is used for this transformation. It is not possible to transform with a composite matrix world-view-projection because the matrix is transformed into homogeneous space and not just world space. The world matrix may contain a scaling transformation, whereas the normal vector of a polygon must be a normalization vector, so normalizing (normalization) must be performed after the transformation.
Diffuse Lighting Vertex Shader
Next, we explain the two new member variables of the VS_OUTPUT structure: normal and lightdirection. Normal is used to pass the transformed face normal vector from the CPU, and lightdirection is a bit special because there is a shader constant called lightdirection. The lightdirection shader constant is a global variable that stores the direction of the light source, while the Lightdirection member in Vs_output represents the light direction of the object surface. Therefore, the global lightdirection is reversed in the vertex shader and assigned to the corresponding output member lightdirection. Of course, you can calculate the correct direction of light in the CPU and then pass it to the vertex shader, so that you do not have to reverse the action in vertex shader. However, in the case of Nvidia FX composer, this must be done because the lighting data is sent by the FX composer to the shader, and to get the correct preview in the render panel, you must reverse the light direction vector in shader. When the lightdirection is reversed, also carried out normalize () operation, which is to ensure that the light direction vector is normalized, if you can ensure that the data passed from the CPU is already normalized, you can omit the normalize ().
Diffuse Lighting Pixel Shader
Although there are some similarities with ambient lighting effect, diffuse lighting pixel shader (see listing 6.4) contains more new code.
List 6.4 The Pixel Shader from diffuselighting.fx
FLOAT4 Pixel_shader (Vs_output in): Sv_target
{
float4 out = (float4) 0;
FLOAT3 normal = normalize (in. Normal);
FLOAT3 lightdirection = Normalize (in. lightdirection);
float n_dot_l = dot (lightdirection, normal);
FLOAT4 color = colortexture.sample (Colorsampler, in. Texturecoordinate);
FLOAT3 ambient = Ambientcolor.rgb * AMBIENTCOLOR.A * COLOR.RGB;
FLOAT3 diffuse = (FLOAT3) 0;
if (n_dot_l > 0)
{
diffuse = Lightcolor.rgb * LIGHTCOLOR.A * n_dot_l * COLOR.RGB;
}
Out.rgb = ambient + diffuse;
OUT.A = COLOR.A;
return out;
}
First, two local variables, color and ambient, were introduced for textrue sampling and calculation of the ambient. Although there is a slight improvement, it is basically the same as the previous step, where the ambient variable is separated for further calculation of the final pixel color.
The following describes the normalization of the member variables of the input parameters, Normal and lightdirection. The data passed to the rasterizer phase is interpolated, and the process causes the vectors to become denormalized. As with the corresponding visual effects, the error caused by the interpolation operation is tiny. Therefore, if you have more stringent performance requirements, simply omit these normalization operations.
Next, assign the dot product result of light direction and surface normal vector to the local variable n_dot_l, which is used to calculate diffuse color below. where if-statement (if condition statement) ensures that the value of the variable n_dot_l is greater than 0. If dot product is negative, it indicates that the light is on the back of the surface, so no calculations are required. The correct dot product value should be between 0.0 and 1.0. A value of 0.0 indicates that the direction of the light is parallel to the surface (there is no lighting effect), and a value of 1.0 indicates that the light direction is perpendicular to the surface (the most intense illumination is obtained). The value of the local variable diffuse is obtained by multiplying sampled color and directional light color,intensity and the dot product previously computed.
Diffuse Lighting Output
The final pixel color is obtained by adding the local variables ambient and diffuse. The alpha channel value uses the alpha value of the color texture directly. Figure 6.5 is the result of applying the diffuse lighting effect to sphere, using the same earth texture as in Figure 6.1. As you can see, the farther away the sphere surfaces is from the light source, the darker the picture appears.
Figure 6.5 diffuselighting.fx applied to a sphere with the texture of Earth. (Original texture from Reto
Stöckli, NASA Earth Observatory. Additional texturing by Nick Zuccarello, Florida Interactive Entertainment
Academy.)
There is a diretional light at the bottom of the picture. NVIDIA FX Composer supports the creation of ambient,point,spot and directional lights in the render panel. To add a different light source, just click on the corresponding button on the toolbar or click Create on the main menu to select the corresponding option. To use directional light in lighting effect, the directional light must be bound to Lightcolor and lightdirection shader constant. This is the function of the previously mentioned object annotations. You can bind light by selecting sphere in the Render panel and clicking the Materialinstance button in the Properties panel (as shown in the 5th button on the Panel toolbar in Figure 6.6,properties). Then select directional Light as the binding object for Directionallight0 and Lightcolor0. When the light binding is bound to the shader constants, any changes to light are immediately displayed in the render panel. For example, rotate light and observe how the illumination area of the sphere changes in the direction of lighting. However, if you just pan change the position of light, you will find no effect because directional lights does not have a real sense of coordinate position. The location of the model used in the render panel to represent directional lights has no effect on the data passed to shader effect.
Figure 6.6 Material Instance Properties within NVIDIA FX Composer.
You can modify the directional by selecting light in the render panel and opening the color picker in the Properties panel. The color and intensity of light (intensity, denoted by the alpha channel). In the output image shown in Figure 6.5, the color of directional light is pure white, and the strength of 1.0,ambient light is 0 (equivalent to disabling ambient light).
Warning
NVIDIA FX Composer supports both automatic and manual binding methods. When automatic binding is available, NVIDIA FX composer will try to bind lights to the most appropriate shader constants. However, this approach does not guarantee the success of the problem. Review the material instance properties and check which ones are already bound and manually modify the settings if necessary. Note, however, that once recomplile effect, all manual bindings will be removed.