热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

DirectX11光照演示示例Demo

光照演示示例Demo1.Demo介绍我们的光照演示程序中,我们实现了3种个不同的光源:1个平行光、1个点光、1个聚光灯。平行光的位置是固定的,点光绕着地形转圈,而聚光灯跟随相机移动,

光照演示示例Demo

1. Demo介绍

我们的光照演示程序中,我们实现了3种个不同的光源:1个平行光、1个点光、1个聚光灯。平行光的位置是固定的,点光绕着地形转圈,而聚光灯跟随相机移动,并指向相机的观察方向。光照演示程序只是对上一章的水波演示程序做了一些修改。

2. effect文件

//=============================================================================
// 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;

// 转换到世界空间
vout.PosW = mul(float4(vin.PosL, 1.0f), gWorld).xyz;
vout.NormalW = mul(vin.NormalL, (float3x3)gWorldInvTranspose);

// 转换到齐次剪裁空间
vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj);

return vout;
}

float4 PS(VertexOut pin) : SV_Target
{
// 插值后的法线需要重新规范化
pin.NormalW = normalize(pin.NormalW);

float3 toEyeW = normalize(gEyePosW - pin.PosW);

// 初始化光照变量
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);

// 每个光源的贡献
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;

// 通常从漫反射材质中提取alpha
litColor.a = gMaterial.Diffuse.a;

return litColor;
}

technique11 LightTech
{
pass P0
{
SetVertexShader( CompileShader( vs_5_0, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_5_0, PS() ) );
}
}

3. C++程序代码

光照计算需要表面法线的信息。我们是在顶点层次定义法线的,然后对法线进行插值用于逐像素光照计算。此外,现在也不需要指定顶点颜色了,表面的颜色可以代入光照方程逐像素地求得。输入布局描述如下所示:

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, 12,
D3D11_INPUT_PER_VERTEX_DATA, 0}
};

在应用程序中,我们定义了三个光源和两个材质。

DirectionalLight mDirLight;
PointLight mPointLight;
SpotLight mSpotLight;
Material mLandMat;
Material mWavesMat;

它们在构造函数中进行初始化:

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::Pi), mPhi(0.1f*MathHelper::Pi), mRadius(80.0f)
{


// 方向光
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);

// 点光的位置随着更新场景函数在每一帧不停地改变位置
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;

// 聚光灯的位置和方向随着更新场景函数在每一帧不停地改变位置
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);
}

当使用多光源时,必须要小心别让颜色超出范围。所以你需要测试环境光、漫反射光和镜面光的强度,对光照范围和衰减的测试也是必须的。有时很容易忘记使用带颜色的灯光,但是它们可以被用来产生不同的效果。例如,你在一个太阳模型上使用平行光,太阳本身被设置为橘黄色,你可以将光的颜色也设置为橘黄色,这样,场景中的物体都会带上橘黄色的色调。异形飞船爆炸时可以加点淡蓝色,红光通常表示紧急情况。

如前所述,点光源和聚光灯是活动的;这一工作在UpdateScene方法中实现:

void LightingApp::UpdateScene(float dt)
{

//
// 光源动画
//

// 让点光源在地面之上转圈
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;


// 聚光灯的位置与观察点的位置相同,光照方向与观察方向相同;
// 这样产生的效果就像是观察者拿着一个手电筒在场景中照来照去一样。
mSpotLight.Position = mEyePosW;
XMStoreFloat3(&mSpotLight.Direction, XMVector3Normalize(target - pos));
}

基本上,点光源会沿着xz平面上的一个圆形轨迹来运动,只是它总保持在地面和水面之上。聚光灯的位置与观察点的位置相同,光照方向与观察方向相同;这样产生的效果就像是观察者拿着一个手电筒在场景中照来照去一样。

最后,我们要在渲染之前将灯光和材质指定给effect:

void LightingApp::DrawScene()
{


// 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 {

mfxMaterial->SetRawValue(&mLandMat, 0, sizeof(mLandMat));
/* 绘制山谷 ...*/

mfxMaterial->SetRawValue(&mWavesMat, 0, sizeof(mWavesMat));
/* 绘制水波...*/
}

HR(mSwapChain->Present(0, 0));
}

4. 计算地形法线

因为我们的地形表面是通过一个函数y = f(x,z)生成的,所以我们可以直接使用微积分计算法线向量,而不是7.2.1节描述的法线平均值技术。要为表面上的每个点计算法线向量,我们必须通过偏导数生成+x和+z方向上的两个正切向量:
这里写图片描述

因为我们的地形表面是通过一个函数y = f(x,z)生成的,所以我们可以直接使用微积分计算法线向量,而不是7.2.1节描述的法线平均值技术。要为表面上的每个点计算法线向量,我们必须通过偏导数生成+x和+z方向上的两个正切向量:
这里写图片描述

我们用于生成地面网格的函数为:

这里写图片描述

偏导数为:

这里写图片描述

表面点(x,f(x,z),z)的表面法线为:

这里写图片描述

注意,表面法线不是规范化向量,在执行照计算之前必须对它进行规范化处理。

我们只在每个顶点上执行上述法线计算,获得顶点法线:

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;
}

对于水体表面来说,法线向量的计算过程基本相同,只是我们没有用于生成水体的公式。不过,读者可以使用有限差异图(finite difference scheme)估算每个顶点上的正切向量(详情请参见[Lengyel02]或任何一本关于数值分析的书籍)。

注意:如果你的微积分已经荒废了,那也不用担心;因为它在本书中的作用不是非常重要。我们在这里使用微积分,只是为了以数学方式生成一个曲面几何体,使我们在演示程序中能够渲染一些比较有趣的物体。在本书最后,我们将为读者讲解如何载入和使用那些由3D建模软件导出的3D网格文件。

5. 程序运行结果截图

项目完整源代码请自行到DirectX11 龙书官网下载,建议使用VS阅读源代码。

这里写图片描述

这里写图片描述


推荐阅读
  • 当使用 `new` 表达式(即通过 `new` 动态创建对象)时,会发生两件事:首先,内存被分配用于存储新对象;其次,该对象的构造函数被调用以初始化对象。为了确保资源管理的一致性和避免内存泄漏,建议在使用 `new` 和 `delete` 时保持形式一致。例如,如果使用 `new[]` 分配数组,则应使用 `delete[]` 来释放内存;同样,如果使用 `new` 分配单个对象,则应使用 `delete` 来释放内存。这种一致性有助于防止常见的编程错误,提高代码的健壮性和可维护性。 ... [详细]
  • 在Android平台中,播放音频的采样率通常固定为44.1kHz,而录音的采样率则固定为8kHz。为了确保音频设备的正常工作,底层驱动必须预先设定这些固定的采样率。当上层应用提供的采样率与这些预设值不匹配时,需要通过重采样(resample)技术来调整采样率,以保证音频数据的正确处理和传输。本文将详细探讨FFMpeg在音频处理中的基础理论及重采样技术的应用。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 深入解析C语言中结构体的内存对齐机制及其优化方法
    为了提高CPU访问效率,C语言中的结构体成员在内存中遵循特定的对齐规则。本文详细解析了这些对齐机制,并探讨了如何通过合理的布局和编译器选项来优化结构体的内存使用,从而提升程序性能。 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 深入解析 Java UTC 时间处理技术与应用 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 本文介绍如何在 Android 中自定义加载对话框 CustomProgressDialog,包括自定义 View 类和 XML 布局文件的详细步骤。 ... [详细]
  • 在配置Nginx的SSL证书后,虽然HTTPS访问能够正常工作,但HTTP请求却会遇到400错误。本文详细解析了这一问题,并提供了Nginx配置的具体示例。此外,还深入探讨了DNS服务器证书、SSL证书的申请与安装流程,以及域名注册、查询方法和CDN加速技术的应用,帮助读者全面了解相关技术细节。 ... [详细]
  • 如何使用 `org.apache.poi.openxml4j.opc.PackagePart` 类中的 `loadRelationships()` 方法及其代码示例详解 ... [详细]
author-avatar
oth0037112
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有