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

这里写图片描述

这里写图片描述


推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
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社区 版权所有