首页 > 代码库 > DirectX11 学习笔记8 - 最简单的光照

DirectX11 学习笔记8 - 最简单的光照

在上一个列子的基础上加了一个地面。这个地面是光照效果生成的。

看图:


先说明:

光照 需要重写一个 lightshader  就是光照的渲染器

// Define the input layout
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
你会发现之前的第二行是Color而不是现在的Normal 。 因为光照的颜色是有外界 环境光 透射光 漫反射光 形成的。

这些光的计算全部写在一个效果文件lightShader.fx里面


//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
cbuffer ConstantBuffer 
{
	matrix World;
	matrix View;
	matrix Projection;
};
cbuffer LightBuffer
{
    float3 cameraPosition;
    float padding;
};
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
	float4 Pos : POSITION;
    float3 Normal : NORMAL;
};
struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VS( VS_INPUT input )
{
    VS_OUTPUT output = (VS_OUTPUT)0;
    float4 worldPosition;
    // 改变顶点为四个分量其次坐标.
    input.Pos.w = 1.0f;
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
     // 世界坐标系中的顶点法向.
    float3 N = mul(input.Normal, (float3x3)World);
	N = normalize(N);
	//世界坐标系顶点位置
	float3 P = output.Pos.xyz;

	//自发射颜色
	float3 emissive = float3(0.0, 0.0, 0.0);
    
	//计算环境光
    float3 ambient =  float3(0.3, 0.3, 0.3);
	
	//计算漫反射光
    float3 L = normalize(float3(-1.0, -1.0, 1.0));
    float diffuseLight = max(dot(N, L), 0);
    float3 diffuse =  diffuseLight;

     //计算高光
    float3 V = normalize(cameraPosition - P);
    float3 H = normalize(L + V);
    float specularLight = pow(max(dot(N, H), 0), 5.0);
 
    if (diffuseLight <= 0) 
	     specularLight = 0;
    float3 specular =  specularLight;

	output.Color.xyz = emissive + ambient + diffuse + specular;
	// float3 tt = float3(1.0, 0.0, 0.0);
	//  output.color.xyz = float3(1.0, 0.0, 0.0);
	output.Color.w = 1.0f;

    return output;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( VS_OUTPUT input ) : SV_Target
{
    return input.Color;
}

主要就是顶点输入进行复杂的光照计算,龙书上有。还多了一个缓冲区

cbuffer LightBuffer
{
    float3 cameraPosition;
    float padding;
};
目前只存了光照的颜色 主要 要为4的倍数 对齐float地址

//create the light buffer
	ZeroMemory( &bd, sizeof(bd) );
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof(LightBuffer);
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bd.CPUAccessFlags = 0;
	hr = device->CreateBuffer( &bd, NULL, &m_lightmaterialBuffer );
	if( FAILED( hr ) )
		return hr;

光照渲染文件里面的缓冲区创建

//
	// Update variables
	//
	ConstantBuffer cb;
	cb.mWorld = XMMatrixTranspose( worldMatrix);
	cb.mView = XMMatrixTranspose( viewMatrix );
	cb.mProjection = XMMatrixTranspose( projectionMatrix );
	deviceContext->UpdateSubresource( m_matrixBuffer, 0, NULL, &cb, 0, 0 );
	deviceContext->VSSetConstantBuffers( 0, 1, &m_matrixBuffer );
	LightBuffer lb;
	lb.cameraPosition=cameraPos;
	deviceContext->UpdateSubresource( m_lightmaterialBuffer, 0, NULL, &lb, 0, 0 );
	deviceContext->VSSetConstantBuffers( 1, 1, &m_matrixBuffer );

比以前多了一个缓冲区更新。

有了光照效果文件  光照渲染器 还差一个承载物

这里是一个四边形,作为大地吧

PlaneModel.h

#pragma  once
#include "XComm.h"
class PlaneModel
{
protected:
	struct SimpleVertexN
		  {
			  XMFLOAT3 Pos;
			  XMFLOAT3 Normal;
		  };
public://顶点缓冲和顶点索引缓冲
	ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
	int m_vertexCount, m_indexCount;
public:
	PlaneModel():m_vertexCount(0),m_indexCount(0){};
	bool init(ID3D11Device*);
	void close();
	void render(ID3D11DeviceContext*);
};

基本跟之前立方体的方法是一摸一样的 只不过把color颜色换成了法向normal

PlaneModel.cpp

#include "PlaneModel.h"
bool PlaneModel::init(ID3D11Device* device)
{
	SimpleVertexN* vertices;
	unsigned long* indices;
	D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
	D3D11_SUBRESOURCE_DATA vertexData, indexData;
	HRESULT result;

	//首先,我们创建2个临时缓冲存放顶点和索引数据,以便后面使用。. 

	// 设置顶点缓冲大小为4,一个平面.
	m_vertexCount = 4;

	// 设置索引缓冲大小.,两个三角形
	m_indexCount = 6;

	// 创建顶点临时缓冲.
	vertices = new SimpleVertexN[m_vertexCount];
	if(!vertices)
	{
		return false;
	}

	// 创建索引缓冲.
	indices = new unsigned long[m_indexCount];
	if(!indices)
	{
		return false;
	}
	//创建顺时针方向的三角形,左手规则
	// 设置顶点数据.
	vertices[0].Pos = XMFLOAT3(-50.0f, -3.0f, -50.0f);  
	vertices[0].Normal = XMFLOAT3(0.0f, 1.0f, 0.0f);

	vertices[1].Pos = XMFLOAT3(-50.0f,-3.0f, 50.0f);  
	vertices[1].Normal = XMFLOAT3(0.0f, 1.0f, 0.0f);

	vertices[2].Pos = XMFLOAT3(50.0f, -3.0f, 50.0f);  
	vertices[2].Normal=  XMFLOAT3(0.0f, 1.0f, 0.0f);

	vertices[3].Pos = XMFLOAT3(50.0f, -3.0f, -50.0f);  
	vertices[3].Normal =  XMFLOAT3(0.0f, 1.0f, 0.0f);


	// 设置索引缓冲数据.
	indices[0] = 0;  // 前面
	indices[1] = 1; 
	indices[2] = 2;  
	indices[3] = 0; 
	indices[4] = 2; 
	indices[5] = 3;  

	// 设置顶点缓冲描述
	vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	vertexBufferDesc.ByteWidth = sizeof(SimpleVertexN) * m_vertexCount;
	vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags = 0;
	vertexBufferDesc.MiscFlags = 0;
	vertexBufferDesc.StructureByteStride = 0;

	// 指向保存顶点数据的临时缓冲.
	vertexData.pSysMem = vertices;
	vertexData.SysMemPitch = 0;
	vertexData.SysMemSlicePitch = 0;

	// 创建顶点缓冲.
	result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
	if(FAILED(result))
	{
		HR(result);
		return false;
	}

	// 设置索引缓冲描述.
	indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
	indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	indexBufferDesc.CPUAccessFlags = 0;
	indexBufferDesc.MiscFlags = 0;
	indexBufferDesc.StructureByteStride = 0;

	// 指向存临时索引缓冲.
	indexData.pSysMem = indices;
	indexData.SysMemPitch = 0;
	indexData.SysMemSlicePitch = 0;

	// 创建索引缓冲.
	result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
	if(FAILED(result))
	{
		HR(result);
		return false;
	}

	// 释放临时缓冲.
	delete [] vertices;
	vertices = 0;

	delete [] indices;
	indices = 0;
}
void PlaneModel::close()
{
	// 释放顶点缓冲.
	if(m_indexBuffer)
	{
		m_indexBuffer->Release();
		m_indexBuffer = 0;
	}

	// 释放索引缓冲
	if(m_vertexBuffer)
	{
		m_vertexBuffer->Release();
		m_vertexBuffer = 0;
	}
}
void PlaneModel::render(ID3D11DeviceContext* deviceContext)
{
	unsigned int stride;
	unsigned int offset;


	// 设置顶点缓冲跨度和偏移.
	stride = sizeof(SimpleVertexN); 
	offset = 0;

	//在input assemberl阶段绑定顶点缓冲,以便能够被渲染
	deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);

	//在input assemberl阶段绑定索引缓冲,以便能够被渲染
	deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);

	// 设置体元语义,渲染三角形列表.
	//注意这里和XModel不一样
	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}


DirectX11 学习笔记8 - 最简单的光照