【DirectX】33-点光源

参考教程

Tutorial 30: Multiple Point Lights

学习记录

  这篇文章中我们将介绍点光源的以及实现多个点光源渲染平面。本次的框架如下:

1

  这次的内容比较简单,我们首先修改 LightClass ,将其改为保存有点光源坐标和颜色的数据类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
////////////////////////////////////////////////////////////////////////////////
// Filename: lightclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _LIGHTCLASS_H_
#define _LIGHTCLASS_H_


//////////////
// INCLUDES //
//////////////
#include <DirectXMath.h>


////////////////////////////////////////////////////////////////////////////////
// Class name: LightClass
////////////////////////////////////////////////////////////////////////////////
class LightClass
{
public:
LightClass();
LightClass(const LightClass&);
~LightClass();

void SetDiffuseColor(float, float, float, float);
void SetPosition(float, float, float);

DirectX::XMFLOAT4 GetDiffuseColor();
DirectX::XMFLOAT4 GetPosition();

private:
DirectX::XMFLOAT4 m_diffuseColor;
DirectX::XMFLOAT4 m_position;
};

#endif

  这只是一个用来保存数据的类,实现暂且不贴出来了。接下来看我们的 LightShaderClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const int NUM_LIGHTS = 4;


////////////////////////////////////////////////////////////////////////////////
// Class name: LightShaderClass
////////////////////////////////////////////////////////////////////////////////
class LightShaderClass
{
private:
struct MatrixBufferType
{
DirectX::XMMATRIX world;
DirectX::XMMATRIX view;
DirectX::XMMATRIX projection;
};

struct LightColorBufferType
{
DirectX::XMFLOAT4 diffuseColor[NUM_LIGHTS];
};

struct LightPositionBufferType
{
DirectX::XMFLOAT4 lightPosition[NUM_LIGHTS];
};


public:
LightShaderClass();
LightShaderClass(const LightShaderClass&);
~LightShaderClass();

bool Initialize(ID3D11Device*, HWND);
void Shutdown();
bool Render(ID3D11DeviceContext*, int, DirectX::XMMATRIX, DirectX::XMMATRIX, DirectX::XMMATRIX,
ID3D11ShaderResourceView*, DirectX::XMFLOAT4*, DirectX::XMFLOAT4*);

private:
bool InitializeShader(ID3D11Device*, HWND, CHAR*, CHAR*);
void ShutdownShader();
void OutputShaderErrorMessage(ID3D10Blob*, HWND, CHAR*);

bool SetShaderParameters(ID3D11DeviceContext*, DirectX::XMMATRIX, DirectX::XMMATRIX, DirectX::XMMATRIX, ID3D11ShaderResourceView*,
DirectX::XMFLOAT4*, DirectX::XMFLOAT4*);
void RenderShader(ID3D11DeviceContext*, int);

private:
ID3D11VertexShader* m_vertexShader;
ID3D11PixelShader* m_pixelShader;
ID3D11InputLayout* m_layout;
ID3D11SamplerState* m_sampleState;
ID3D11Buffer* m_matrixBuffer;
ID3D11Buffer* m_lightColorBuffer;
ID3D11Buffer* m_lightPositionBuffer;
};

  我们新增加了两个结构体,LightColorBufferType 用来保存光颜色,LightPositionBufferType 用来保存光位置。需要注意到的是我们的 RenderSetShaderParameters 的参数修改为需要接受两个 FLOAT4 数组,用来向光颜色数组和光位置数组传值。

  在 InitializeShader 方法中,这次我们要创建 matrixBufferlightColorBufferlightPositionBuffer 三个缓冲区,其中 matrixBufferlightPositionBuffer 两个将传给顶点着色器, lightColorBuffer 传给像素着色器。其实现如下(只列出部分):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
bool LightShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, CHAR* vsFilename, CHAR* psFilename)
{
...
// Setup the description of the matrix dynamic constant buffer that is in the vertex shader.
matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
matrixBufferDesc.MiscFlags = 0;
matrixBufferDesc.StructureByteStride = 0;

// Create the matrix constant buffer pointer so we can access the vertex shader constant buffer from within this class.
result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer);
if(FAILED(result))
{
return false;
}

// Setup the description of the light dynamic constant buffer that is in the pixel shader.
// Note that ByteWidth always needs to be a multiple of 16 if using D3D11_BIND_CONSTANT_BUFFER or CreateBuffer will fail.
lightColorBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
lightColorBufferDesc.ByteWidth = sizeof(LightColorBufferType);
lightColorBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
lightColorBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
lightColorBufferDesc.MiscFlags = 0;
lightColorBufferDesc.StructureByteStride = 0;

// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
result = device->CreateBuffer(&lightColorBufferDesc, NULL, &m_lightColorBuffer);
if(FAILED(result))
{
return false;
}

// Setup the description of the light dynamic constant buffer that is in the pixel shader.
// Note that ByteWidth always needs to be a multiple of 16 if using D3D11_BIND_CONSTANT_BUFFER or CreateBuffer will fail.
lightPositionBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
lightPositionBufferDesc.ByteWidth = sizeof(LightPositionBufferType);
lightPositionBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
lightPositionBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
lightPositionBufferDesc.MiscFlags = 0;
lightPositionBufferDesc.StructureByteStride = 0;

// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
result = device->CreateBuffer(&lightPositionBufferDesc, NULL, &m_lightPositionBuffer);
if(FAILED(result))
{
return false;
}

return true;
}

  在 SetShaderParameters 中,我们动态的为其赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
bool LightShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, DirectX::XMMATRIX worldMatrix, DirectX::XMMATRIX viewMatrix, 
DirectX::XMMATRIX projectionMatrix, ID3D11ShaderResourceView* texture,
DirectX::XMFLOAT4 lightColor[], DirectX::XMFLOAT4 lightPosition[])
{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
MatrixBufferType* dataPtr;
LightColorBufferType* dataPtr2;
LightPositionBufferType* dataPtr3;
unsigned int bufferNumber;


// Set shader texture resource in the pixel shader.
deviceContext->PSSetShaderResources(0, 1, &texture);

// Lock the matrix constant buffer so it can be written to.
result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}

// Get a pointer to the data in the constant buffer.
dataPtr = (MatrixBufferType*)mappedResource.pData;

// Transpose the matrices to prepare them for the shader.
worldMatrix = DirectX::XMMatrixTranspose(worldMatrix);
viewMatrix = DirectX::XMMatrixTranspose(viewMatrix);
projectionMatrix = DirectX::XMMatrixTranspose(projectionMatrix);

// Copy the matrices into the constant buffer.
dataPtr->world = worldMatrix;
dataPtr->view = viewMatrix;
dataPtr->projection = projectionMatrix;

// Unlock the matrix constant buffer.
deviceContext->Unmap(m_matrixBuffer, 0);

// Set the position of the matrix constant buffer in the vertex shader.
bufferNumber = 0;

// Now set the matrix constant buffer in the vertex shader with the updated values.
deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);

// Lock the light constant buffer so it can be written to.
result = deviceContext->Map(m_lightColorBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}

// Get a pointer to the data in the constant buffer.
dataPtr2 = (LightColorBufferType*)mappedResource.pData;

// Copy the lighting variables into the constant buffer.
for (int i = 0 ; i < NUM_LIGHTS ; ++i) {
dataPtr2->diffuseColor[i] = lightColor[i];
}

// Unlock the constant buffer.
deviceContext->Unmap(m_lightColorBuffer, 0);

// Set the position of the light constant buffer in the pixel shader.
bufferNumber = 0;

// Finally set the light constant buffer in the pixel shader with the updated values.
deviceContext->PSSetConstantBuffers(bufferNumber, 1, &m_lightColorBuffer);

// Lock the light constant buffer so it can be written to.
result = deviceContext->Map(m_lightPositionBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}

// Get a pointer to the data in the constant buffer.
dataPtr3 = (LightPositionBufferType*)mappedResource.pData;

// Copy the lighting variables into the constant buffer.
for (int i = 0 ; i < NUM_LIGHTS ; ++i) {
dataPtr3->lightPosition[i] = lightPosition[i];
}

// Unlock the constant buffer.
deviceContext->Unmap(m_lightPositionBuffer, 0);

// Set the position of the light constant buffer in the vertex shader.
bufferNumber = 1;

// Finally set the light constant buffer in the vertex shader with the updated values.
deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_lightPositionBuffer);

return true;
}

  在对应的着色器代码中,我们在顶点着色器中对光源坐标进行变换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/////////////
// DEFINES //
/////////////
#define NUM_LIGHTS 4

/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};

cbuffer LightPositionBuffer
{
float4 lightPosition[NUM_LIGHTS];
};

//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
float4 position : POSITION;
float2 tex : TEXCOORD0;
float3 normal : NORMAL;
};

struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
float3 normal : NORMAL;
float3 lightPos1 : TEXCOORD1;
float3 lightPos2 : TEXCOORD2;
float3 lightPos3 : TEXCOORD3;
float3 lightPos4 : TEXCOORD4;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType LightVertexShader(VertexInputType input)
{
PixelInputType output;
float4 worldPosition;

// Change the position vector to be 4 units for proper matrix calculations.
input.position.w = 1.0f;

// Calculate the position of the vertex against the world, view, and projection matrices.
output.position = mul(input.position, worldMatrix);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);

// Store the texture coordinates for the pixel shader.
output.tex = input.tex;

// Calculate the normal vector against the world matrix only.
output.normal = mul(input.normal, (float3x3)worldMatrix);

// Normalize the normal vector.
output.normal = normalize(output.normal);

worldPosition = mul(input.position, worldMatrix);

output.lightPos1.xyz = lightPosition[0].xyz - worldPosition.xyz;
output.lightPos2.xyz = lightPosition[1].xyz - worldPosition.xyz;
output.lightPos3.xyz = lightPosition[2].xyz - worldPosition.xyz;
output.lightPos4.xyz = lightPosition[3].xyz - worldPosition.xyz;

output.lightPos1 = normalize(output.lightPos1);
output.lightPos2 = normalize(output.lightPos2);
output.lightPos3 = normalize(output.lightPos3);
output.lightPos4 = normalize(output.lightPos4);

return output;
}

  像素着色器中我们使用四个光混合进行计算最终颜色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/////////////
// DEFINES //
/////////////
#define NUM_LIGHTS 4

/////////////
// GLOBALS //
/////////////
Texture2D shaderTexture;
SamplerState SampleType;

cbuffer LightColorBuffer
{
float4 diffuseColor[NUM_LIGHTS];
};

//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
float3 normal : NORMAL;
float3 lightPos1 : TEXCOORD1;
float3 lightPos2 : TEXCOORD2;
float3 lightPos3 : TEXCOORD3;
float3 lightPos4 : TEXCOORD4;
};


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 LightPixelShader(PixelInputType input) : SV_TARGET
{
float4 texColor;
float lightIntensity1, lightIntensity2, lightIntensity3, lightIntensity4;
float4 color1, color2, color3, color4;

lightIntensity1 = saturate(dot(input.normal, input.lightPos1));
lightIntensity2 = saturate(dot(input.normal, input.lightPos2));
lightIntensity3 = saturate(dot(input.normal, input.lightPos3));
lightIntensity4 = saturate(dot(input.normal, input.lightPos4));

color1 = diffuseColor[0] * lightIntensity1;
color2 = diffuseColor[1] * lightIntensity2;
color3 = diffuseColor[2] * lightIntensity3;
color4 = diffuseColor[3] * lightIntensity4;

texColor = shaderTexture.Sample(SampleType, input.tex);

return saturate(color1 + color2 + color3 + color4) * texColor;;
}

  最后,修改我们的 GraphicsClass ,成员变量新加入了四个光源对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
////////////////////////////////////////////////////////////////////////////////
// Class name: GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
GraphicsClass();
GraphicsClass(const GraphicsClass&);
~GraphicsClass();

bool Initialize(int, int, HWND);
void Shutdown();
bool Frame();
bool Render();

private:


private:
D3DClass* m_D3D;
CameraClass* m_Camera;
ModelClass *m_Model;
LightClass *m_Light1 , *m_Light2 , *m_Light3 , *m_Light4;
LightShaderClass* m_LightShader;
};

  在 Initialize 中初始化 light 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
bool result;
...
m_Light1 = new LightClass;
if (!m_Light1) {
return false;
}
m_Light1->SetDiffuseColor(1.0f, 0.0f, 0.0f, 1.0f);
m_Light1->SetPosition(-10.0f, 1.0f, 10.0f);

m_Light2 = new LightClass;
if (!m_Light2) {
return false;
}
m_Light2->SetDiffuseColor(0.0f, 1.0f, 0.0f, 1.0f);
m_Light2->SetPosition(10.0f, 1.0f, 10.0f);

m_Light3 = new LightClass;
if (!m_Light3) {
return false;
}
m_Light3->SetDiffuseColor(0.0f, 0.0f, 1.0f, 1.0f);
m_Light3->SetPosition(-10.0f, 1.0f, -10.0f);

m_Light4 = new LightClass;
if (!m_Light4) {
return false;
}
m_Light4->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
m_Light4->SetPosition(10.0f, 1.0f, -10.0f);

return true;
}

  在 Render 里进行渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
bool GraphicsClass::Render()
{
bool result;

XMMATRIX worldMatrix, viewMatrix, projectionMatrix;
XMFLOAT4 diffusecolor[4];
XMFLOAT4 lightPosition[4];

diffusecolor[0] = m_Light1->GetDiffuseColor();
diffusecolor[1] = m_Light2->GetDiffuseColor();
diffusecolor[2] = m_Light3->GetDiffuseColor();
diffusecolor[3] = m_Light4->GetDiffuseColor();

lightPosition[0] = m_Light1->GetPosition();
lightPosition[1] = m_Light2->GetPosition();
lightPosition[2] = m_Light3->GetPosition();
lightPosition[3] = m_Light4->GetPosition();


m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
m_Camera->Render();

m_D3D->GetWorldMatrix(worldMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);
m_Camera->GetViewMatrix(viewMatrix);

m_Model->Render(m_D3D->GetDeviceContext());

worldMatrix *= XMMatrixScaling(10.0f , 0.1f , 10.0f);

result = m_LightShader->Render(
m_D3D->GetDeviceContext(),
m_Model->GetIndexCount(),
worldMatrix,
viewMatrix,
projectionMatrix,
m_Model->GetTexture(),
diffusecolor,
lightPosition);
if (!result) {
return false;
}

m_D3D->EndScene();

return true;
}

  最终效果如下:

2

0%