Iluminación directa 3D

composición de luz

Luz ambiental: Este tipo de luz llega a la superficie del objeto después de reflejarse en otras superficies e ilumina toda la escena. Si desea simular aproximadamente este tipo de luz reflejada a bajo costo, la luz ambiental es una buena opción.

Luz difusa: este tipo de luz viaja en una dirección específica. Cuando alcanza una determinada superficie, se reflejará uniformemente en todas las direcciones. No importa desde qué dirección se mire, el brillo de la superficie será el mismo. Por lo tanto, no es necesario considerar la posición del observador al utilizar este modelo. De este modo, en la ecuación de la luz difusa sólo es necesario considerar la propagación de la luz. La dirección y la orientación de la superficie de la luz emitida por una fuente de luz son generalmente de este tipo.

Luz especular: este tipo de luz viaja en una dirección específica. Cuando dicha luz llega a una superficie, se reflejará estrictamente en otra dirección, lo que resulta en una iluminación de alto brillo que solo se puede observar dentro de un cierto rango de ángulo, por lo que en la En la ecuación de iluminación especular, no solo se debe considerar la dirección incidente de la luz y la orientación de la superficie de la primitiva, sino también la posición del punto de observación. Las luces especulares se pueden utilizar para simular reflejos en un objeto, como los que se producen cuando la luz incide sobre una superficie pulida.

La luz especular requiere más cálculos que otros tipos de luz, por lo que Direct3D proporciona una opción de cambio. De forma predeterminada, Direct no realiza cálculos de reflexión especular. Si desea habilitar la luz especular, debe llamar a la interfaz para configurarla.

Device->SetRenderState(D3DRS_SPECULARENABLE, true);

Cada tipo de luz se puede representar mediante la estructura D3DCOLORVALUE o D3DXCOLOR. Al describir el color de la luz, se ignorará el valor Alfa en la clase D3DXCOLOR.

D3DXCOLOR redAmbient(1.0f, 0.0f, 0.0f, 1.0f);
D3DXCOLOR blueDiffuse(0.0f, 0.0f, 1.0f, 1.0f);
D3DXCOLOR whiteSpecular(1.0f, 1.0f, 1.0f, 1.0f);

Material

En el mundo real, el color de un objeto que observamos está determinado por el color de la luz reflejada por el objeto. Por ejemplo, una esfera roja pura refleja toda la luz incidente roja y absorbe toda la luz que no es roja, por lo que parece roja. . Cuando un objeto absorbe toda la luz, aparece negro. Si un objeto puede reflejar el 100% de la luz roja, verde y azul, aparecerá blanco. Direct3D simula el mismo fenómeno definiendo el material del objeto. El material nos permite define la relación de reflexión de varios colores de luz en la superficie del objeto . El material está representado por la estructura D3DMATERIAL9.

typedef struct D3DMATERIAL9 {
    D3DCOLORVALUE   Diffuse;  
    D3DCOLORVALUE   Ambient;  
    D3DCOLORVALUE   Specular; 
    D3DCOLORVALUE   Emissive; 
    float           Power;    
} D3DMATERIAL9;

Difuso : Especifique la reflectividad del material para la luz difusa.
Ambiente : Especifique la reflectividad del material para la luz ambiental.
Especular : Especifique la reflectividad del material para la luz especular.
Emisivo : Este componente se utiliza para mejorar el brillo del objeto, haciendo que parezca que puede emitir luz por sí mismo.
Potencia : Especifica la nitidez del punto de resaltado especular. Cuanto mayor sea el valor, mayor será la nitidez del punto de resaltado.

//只反射红色光
D3DMATERIAL9 red;
::ZeroMemory(&red, sizeof(red));
red.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Ambient= D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Specular = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
red.Power = 5.0f;

Establecer la interfaz del material SetMaterial

D3DMATERIAL9 blueMaterial;
Device->SetMaterial(&blueMaterial);
//draw...

vértice normal

La normal del vértice describe la normal de cada vértice que forma el polígono. Direct3D necesita conocer la dirección normal del vértice para determinar el ángulo de incidencia cuando la luz llega a la superficie. Dado que el cálculo de iluminación se realiza para cada vértice, Direct3D necesita conocer la superficie. La orientación local (dirección normal) en cada vértice. Describir un vértice normal requiere modificar la estructura del vértice.

struct Vertex
{
	float _x, _y, _z;
	float _nx, _ny, _nz;
	static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;

El cálculo de las normales de los tres vértices de un triángulo se puede obtener calculando el vector normal de la superficie, primero se calculan los dos vectores ubicados en el plano del triángulo.

void ComputeNormal(D3DXVECTOR3* p0, D3DXVECTOR3* p1, D3DXVECTOR3* p2, D3DXVECTOR3* out)
{
	D3DXVECTOR3 u = *p1 - *p0;
	D3DXVECTOR3 v = *p2 - *p0;
	D3DXVec3Cross(out, &u, &v);
	D3DXVec3Normalize(out, out);
}

Cuando se usan unidades triangulares para aproximar una superficie, usar el vector normal del parche como el vector normal del vértice que constituye el parche no puede producir un efecto muy suave. Una mejor manera de encontrar el vector normal del vértice es calcular la media de los vectores normales. Necesitas encontrar los vectores normales a la superficie de todos los triángulos que comparten el punto v, luego sumar estos vectores normales y dividirlos por el número para promediar.

Durante el proceso de transformación, es posible que las normales de los vértices ya no sean canónicas, por lo que la mejor manera es volver a normalizar estableciendo el estado de dibujo después de que se complete la transformación.

Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);

fuente de luz

Direct3D admite 3 tipos de fuentes de luz: fuente de luz puntual, luz direccional y foco.

Fuente de luz puntual : esta fuente de luz tiene una posición fija en el sistema de coordenadas mundial y emite luz en todas las direcciones (D3DLIGHT_POINT)
Luz direccional : esta fuente de luz no tiene información de posición y los rayos de luz emitidos se propagan paralelos entre sí en una dirección específica. (D3DLIGHT_DIRECTIONAL)
Foco : Este tipo de fuente de luz es similar a una linterna. La fuente de luz tiene información de posición y la luz que emite se propaga en forma de cono a lo largo de una dirección específica (D3DLIGHT_SPOT)

typedef struct D3DLIGHT9 {
    D3DLIGHTTYPE    Type;        
    D3DCOLORVALUE   Diffuse;     
    D3DCOLORVALUE   Specular;    
    D3DCOLORVALUE   Ambient;     
    D3DVECTOR       Position;    
    D3DVECTOR       Direction;   
    float           Range;       
    float           Falloff;     
    float           Attenuation0;
    float           Attenuation1;
    float           Attenuation2;
    float           Theta;       
    float           Phi;         
} D3DLIGHT9;

Tipo : define el tipo de fuente de luz creada, que puede tomar los siguientes tres valores de enumeración: D3DLIGHT_POINT , D3DLIGHT_SPOT , D3DLIGHT_DIRECTIONAL
Difusa : el color de la luz difusa emitida por la fuente de luz
Especular : el color de la luz especular emitida por la fuente de luz
Ambiente : El color de la luz especular emitida por la fuente de luz El color de la luz ambiental.
Posición : un vector utilizado para describir la posición de la fuente de luz en el sistema de coordenadas mundial. Este parámetro no tiene sentido para la luz direccional.
Dirección : un vector que describe la dirección de propagación de la luz en el sistema de coordenadas mundial. Este parámetro no tiene sentido para fuentes de luz puntuales.
Rango : La trayectoria óptica máxima que la luz puede alcanzar antes de "morir". El valor máximo de este valor es. \sqrt{FLT\_MAX}Para luz direccional, este parámetro no tiene sentido.
Falloff : este valor se utiliza para focos. Este parámetro define la intensidad de la luz desde el cono interior al cono exterior. La primera versión de este parámetro se establece en 1,0f. Estas variables de atenuación
Attenuation0 , Attenuation1 y Attenuation2 definen la forma en que la intensidad de la luz se atenúa con la distancia. Estas variables solo se usan para fuentes de luz puntuales y focos, y representan el coeficiente de atenuación de distancia constante, lineal y cuadrático, la fórmula de atenuación es , donde D es la distancia desde la fuente de luz atenuación=\frac{1}{A_{0}+A_{1}\cdot D+A_{2}\cdot D^{2}}a el vértice.
Theta : Solo para focos, especifica el ángulo del cono interior en radianes.
Phi : solo para focos, especifica el ángulo del cono exterior en radianes.

De manera similar a la inicialización de la estructura D3DMATERIAL9, la inicialización de la estructura D3DLIGHT9 también es muy engorrosa cuando se requiere una fuente de luz simple.

D3DLIGHT9 d3d::InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)
{
	D3DLIGHT9 light;
	::ZeroMemory(&light, sizeof(light));
	light.Type = D3DLIGHT_DIRECTIONAL;
	light.Ambient = *color * 0.4f;
	light.Diffuse = *color;
	light.Specular = *color * 0.6f;
	light.Direction = *direction;
	return light;
}

D3DXVECTOR3 dir(1.0f, 0.0f, 0.0f);
D3DXCOLOR c = d3d::WHITE;
D3DLIGHT9 dirLight = d3d::InitDirectionalLight(&dir, &c);

Después de inicializar la instancia D3DLIGHT9, debemos registrar la fuente de luz que se utilizará en una lista interna de fuentes de luz mantenida por Direct3D. Después del registro exitoso, podemos controlar su estado de encendido/apagado.

Device->SetLight(0, &light);
Device->LightEnable(0, true);

Rutina de iluminación

bool SetUpPyramid()
{
	//启用光照(默认是启用的)
	Device->SetRenderState(D3DRS_LIGHTING, true);

	Device->CreateVertexBuffer(12 * sizeof(PyramidVertex), D3DUSAGE_WRITEONLY, PyramidVertex::FVF, D3DPOOL_MANAGED, &Pyramid, 0);

	PyramidVertex* v;
	Pyramid->Lock(0, 0, (void**)&v, 0);

	v[0] = PyramidVertex(-1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);
	v[1] = PyramidVertex(0.0f, 1.0f, 0.0f, 0.0f, 0.707f, -0.707f);
	v[2] = PyramidVertex(1.0f, 0.0f, -1.0f, 0.0f, 0.707f, -0.707f);

	v[3] = PyramidVertex(-1.0f, 0.0f, 1.0f, -0.707f, 0.707f, 0.0f);
	v[4] = PyramidVertex(0.0f, 1.0f, 0.0f, -0.707f, 0.707f, 0.0f);
	v[5] = PyramidVertex(-1.0f, 0.0f, -1.0f, -0.707f, 0.707f, 0.0f);

	v[6] = PyramidVertex(1.0f, 0.0f, -1.0f, 0.707f, 0.707f, 0.0f);
	v[7] = PyramidVertex(0.0f, 1.0f, 0.0f, 0.707f, 0.707f, 0.0f);
	v[8] = PyramidVertex(1.0f, 0.0f, 1.0f, 0.707f, 0.707f, 0.0f);

	v[9] = PyramidVertex(1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);
	v[10] = PyramidVertex(0.0f, 1.0f, 0.0f, 0.0f, 0.707f, 0.707f);
	v[11] = PyramidVertex(-1.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.707f);
	Pyramid->Unlock();

	D3DMATERIAL9 mtrl;
	mtrl.Ambient = d3d::WHITE;
	mtrl.Diffuse = d3d::WHITE;
	mtrl.Specular= d3d::WHITE;
	mtrl.Emissive = d3d::BLACK;
	mtrl.Power = 5.0f;
	Device->SetMaterial(&mtrl);

	D3DLIGHT9 dir;
	::ZeroMemory(&dir, sizeof(dir));
	dir.Type = D3DLIGHT_DIRECTIONAL;
	dir.Diffuse = d3d::WHITE;
	dir.Specular = d3d::WHITE * 0.3f;
	dir.Ambient = d3d::WHITE * 0.6f;
	dir.Direction = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
	Device->SetLight(0, &dir);
	Device->LightEnable(0, true);

	//规范化法向量
	Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
	//启用镜面高光
	Device->SetRenderState(D3DRS_SPECULARENABLE, true);

	//取景变换(观察者坐标系)
	D3DXVECTOR3 position(3.0f, 2.0f, -3.0f);
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXMATRIX V;
	D3DXMatrixLookAtLH(&V, &position, &target, &up);
	Device->SetTransform(D3DTS_VIEW, &V);
	
	//投影变换
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI*0.5f, (float)Width / (float)Height, 1.0f, 1000.0f);
	Device->SetTransform(D3DTS_PROJECTION, &proj);
	return true;
}

Supongo que te gusta

Origin blog.csdn.net/SwordArcher/article/details/132720227
Recomendado
Clasificación