热门标签 | 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阅读源代码。

这里写图片描述

这里写图片描述


推荐阅读
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 本教程涵盖OpenGL基础操作及直线光栅化技术,包括点的绘制、简单图形绘制、直线绘制以及DDA和中点画线算法。通过逐步实践,帮助读者掌握OpenGL的基本使用方法。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 本文介绍如何使用Objective-C结合dispatch库进行并发编程,以提高素数计数任务的效率。通过对比纯C代码与引入并发机制后的代码,展示dispatch库的强大功能。 ... [详细]
  • 火星商店问题:线段树分治与持久化Trie树的应用
    本题涉及编号为1至n的火星商店,每个商店有一个永久商品价值v。操作包括每天在指定商店增加一个新商品,以及查询某段时间内某些商店中所有商品(含永久商品)与给定密码值的最大异或结果。通过线段树分治和持久化Trie树来高效解决此问题。 ... [详细]
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
  • 本题通过将每个矩形视为一个节点,根据其相对位置构建拓扑图,并利用深度优先搜索(DFS)或状态压缩动态规划(DP)求解最小涂色次数。本文详细解析了该问题的建模思路与算法实现。 ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 本文探讨了如何在 F# Interactive (FSI) 中通过 AddPrinter 和 AddPrintTransformer 方法自定义类型(尤其是集合类型)的输出格式,提供了详细的指南和示例代码。 ... [详细]
  • 历经三十年的开发,Mathematica 已成为技术计算领域的标杆,为全球的技术创新者、教育工作者、学生及其他用户提供了一个领先的计算平台。最新版本 Mathematica 12.3.1 增加了多项核心语言、数学计算、可视化和图形处理的新功能。 ... [详细]
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社区 版权所有