光照演示程序(第八章内容)

光照演示程序(第八章内容)

8.14、光照演示程序

本章演示程序基于上一章的“陆地与波浪演示程序”的基础上构建而成的,其中利用了一个方向光来表示太阳,用户可以使用方向键来控制太阳的方向。

8.14.1、顶点格式

光照的计算需要依赖于表面法线,所以我们会在顶点层级定义法线,方便在光栅化过程中进行插值计算,由此展开逐像素光照。同时我们也不需要指定顶点的颜色,而是以每一个像素应用光照方程之后所生成的像素颜色代替指定顶点颜色。下面是顶点结构体:

//c++顶点结构体
struct Vertex
{
    DirectX::XMFLOAT3 Pos;
    DirectX::XMFLOAT3 Noraml;
}

//对应的HLSL顶点结构体
struct VertexIn
{
    float3 PosL : POSITION;
    float3 NormalL : NORMAL;
}

//新的输入布局描述
mInputLayout =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
        { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
    };

8.14.2、计算法线

GeometryGenerator类中可以用于生成各种几何形状的函数,已经可以通过顶点法线去创建对应的图形数据,不过,为了使地形(terrain)拥有更加真实的表面,我们将会为地形生成法向量。

因为地形曲面的函数
\[
y = (x, z);
\]
已经给出来了,所以对于曲面上的任何一个点,我们都可以通过偏导数在+x和+z上建立两个切向量(tangent vector),因为这两个向量都位于曲面的切平面上,所以我们可以通过求这两个向量的叉乘来求取该点的法向量
\[
用于生成陆地网格的函数:f(x, z) = 0.3z·sin(0.1x) + 0.3x·cos(0.1z)
\]
则偏导数为:
\[
对x的偏导数:0.03z·cos(0.1x) + 0.3·cos(0.1z)
\]

\[
对y的偏导数:0.3·sin(0.1x) - 0.03x·sin(0.1z)
\]

此处跳过求两个切向量的叉乘步骤。

则位于曲面上一点(x, f(x, z), z)的曲面法线为:
\[
n(x, z) = (-w,1,-s);
\]
w为对x的偏导数,s为对y的偏导数。可以很清楚的看出,上述法线并不具有单位长度,所以在进行光照计算之前,我们需要对上述法线进行规格化处理。

我们要对地形的每一个顶点进行上述的法线计算,以获取他们对应的顶点法线:

XMFLOAT3 LitWavesApp::GetHillsNormal(float x, float z)const
{
    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;
}

8.14.3、更新光照的方向

在8.13.7节中,我们将Light数组妨碍渲染过程常量缓冲区中。在演示程序中,我们使用一个方向光来表示太阳,并允许用户使用方向键来控制光源的方位。这也就是说,我们需要在每一帧更新阳光照射的方向,并且将结果设置到渲染过程常量中。

我们这里使用球坐标来追踪太阳的位置,但是由于太阳的距离使无限远的,所以对于径向距离的取值是无关紧要的。在实例程序中,我们将径向距离设置为1,使太阳始终在单位球体这一轨道上运动。下列代码用于更新方向光源方位:

float mSunTheta = 1.25f*XM_PI;
float mSunPhi = XM_PIDIV4;

void LitWavesApp::OnKeyboardInput(const GameTimer& gt)
{
    const float dt = gt.DeltaTime();

    if(GetAsyncKeyState(VK_LEFT) & 0x8000)
        mSunTheta -= 1.0f*dt;

    if(GetAsyncKeyState(VK_RIGHT) & 0x8000)
        mSunTheta += 1.0f*dt;

    if(GetAsyncKeyState(VK_UP) & 0x8000)
        mSunPhi -= 1.0f*dt;

    if(GetAsyncKeyState(VK_DOWN) & 0x8000)
        mSunPhi += 1.0f*dt;

    mSunPhi = MathHelper::Clamp(mSunPhi, 0.1f, XM_PIDIV2);
}

void LitWavesApp::UpdateMainPassCB(const GameTimer& gt)
{
    ……
    XMVECTOR lightDir = -MathHelper::SphericalToCartesian(1.0f, mSunTheta, mSunPhi);

    XMStoreFloat3(&mMainPassCB.Lights[0].Direction, lightDir);
    mMainPassCB.Lights[0].Strength = { 1.0f, 1.0f, 0.9f };

    auto currPassCB = mCurrFrameResource->PassCB.get();
    currPassCB->CopyData(0, mMainPassCB);
}

8.14.4、更新根签名

为了实现光照,我们为着色器引入了一个材质常量缓冲区,为了支持这个新引入的常量缓冲区,我们需要改写之前的根签名。

void LitWavesApp::BuildRootSignature()
{
    // 创建根参数(根参数可以是描述符表,根描述符,根常量)
    CD3DX12_ROOT_PARAMETER slotRootParameter[3];

    // 创建根CBV
    slotRootParameter[0].InitAsConstantBufferView(0);
    slotRootParameter[1].InitAsConstantBufferView(1);
    slotRootParameter[2].InitAsConstantBufferView(2);

    // 根签名是一系列根参数组成的
    CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(3, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

    ……
}

8.14.5、程序运行效果图

8.15、小节

  1. 运用光照之后,我们就可以不必再指定每一个顶点的颜色,取而代之的是要定义场景光源和每一个顶点的材质。我们可以将材质看作是确定光如何和物体表面进行交互的一种属性。通过对三角形表面的每个顶点处的材质进行线性插值计算,便可以获得三角形网格中每一个表面点处的顶点数据。
  2. 曲面法线是一种正交于曲面上某一点处切平面的单位向量,利用曲面法线可以确定曲面上某一点的“朝向”。为了获取三角形网格表面每一个点的曲面法线,我们需要指定顶点的曲面法线,然后在光栅化过程中对三角形中的这些顶点法线进行线性插值。我们一般使用一种叫做求法线平均值的计算方法来估算顶点法线。如果矩阵A可用于变换点和向量,那么A的逆转置矩阵可以用来变换经非等比变换或剪切变换之后的法线。
  3. 平行光源(方向光源)模拟了一种距离被照物体极远的光源,比如太阳,点光源会向四周各个方向发射光,比如电灯泡,聚光灯光源的发光范围是圆锥体,比如手电筒。
  4. 根据菲涅尔效应可知,当光线到达两种不同折射率介质之间的界面时,一部分光会被反射,另一部分光会折射进入介质里面,反射的光量依赖于介质和表面法向量于光向量之间的夹角。由于计算的复杂想,菲涅尔方程一般不会应用于实时渲染,而是采用石里克近似替代菲涅尔方程。
  5. 现实世界中的反射物体一般都不是理想镜面,都是具有一定粗糙度的。我们可以把理想镜面的粗糙度视为0,并且它的宏观表面法线和微观表面法线都指向相同的地方,随着粗糙度的增加,微观表面法线逐渐偏离宏观表面法线,并导致反射光逐渐扩展成一个镜面瓣(粗糙度令镜面反射光扩散开来,镜面反射光的范围称为镜面瓣)
  6. 环境光模拟了在场景中场景中进行多次散射和反弹,然后按各个方向均等的射向物体的间接光。漫反射光模拟的时进入介质内部的光,其中一部分会被吸收,剩下的部分则会散射会表面。镜面光模拟的是根据菲涅尔效应和表面粗糙度而从表面反射的光。

原文地址:https://www.cnblogs.com/yaya12138/p/11909365.html

时间: 2024-11-10 11:09:30

光照演示程序(第八章内容)的相关文章

全局光照技术进化史1-光线追踪篇

本来计划是最近一年专心写书,不要花心思和精力写博客的,因为写一篇优质的博客文章其实也要花费不少的时间构思的:单篇博客虽然文字少但是你可能需要花费更多的精力在有限的篇幅内包括更多上下文信息,以及更精简地组织内容,在我看来它的创作付出不亚于图书内容写作(当然如果作者对自己要求没那么严谨的话可能也没那么严重). <游戏引擎全局光照技术>采取了一种新的出版形式,它从写作第一章开始,就积极和社区互动并开始宣传,其方式和游戏发行的思路一致:即在开发阶段不断推出测试版积极和玩家互动,并收集反馈信息进行持续改

全局光照:光线追踪、路径追踪与GI技术进化编年史

全局光照(Global Illumination,简称 GI), 作为图形学中比较酷的概念之一,是指既考虑场景中来自光源的直接光照,又考虑经过场景中其他物体反射后的间接光照的一种渲染技术. 大家常听到的光线追踪,路径追踪等同样很酷的概念,都是全局光照中人气较高的算法流派. 而这篇文章将围绕全局光照技术,介绍的要点有: 全局光照的基本概念 全局光照的算法主要流派 全局光照技术进化编年史 光线追踪 Ray Tracing 路径追踪 Path Tracing 光线追踪.路径追踪.光线投射的区别 环境光

《Spring实战》学习笔记-第八章:使用Spring Web Flow

第四版的第八章内容与第三版基本一致. 本章内容: 创建会话式web应用程序 定义流程状态和行为 保护web流程 互联网的一个奇特之处就在于它很容易让人迷失.有如此多的内容可以查看和阅读,而超链接是其强大魔力的核心所在. 有时候,web应用程序需要控制web冲浪者的导向,引导他们一步步地访问应用.比如电子商务网站的付款流程,从购物车开始,应用程序会引导你依次经过配送详情.账单信息以及最终的订单确认. Spring Web Flow是一个web框架,它适用于元素规定流程运行的程序.本章中,我们将会探

《程序是怎样跑起来的》第八章

在第八章内容,从源文件到可执行文件的学习中,我了解到了一些知识.如1.计算机只能运行本地代码,cpu直接解析并运行的不是源代码而是本地代码的程序.不同编程语言编写的代码,转换成本地代码后,都变成一种语言(机器语言)来表示了.2.本地代码的内容就是数值的罗列.计算机指令也是数值的罗列.3.编译后生成的不是exe.文件,而是扩展名为”.obj"的目标文件.4.存储着Sprintf()的目标文件的cw321lib就是静链接库.Sprintf()提供了通过指定格式把数值的转换成字符串的功能.5.可执行文

StackOverflow程序员推荐:每个程序员都应读的30本书

“如果能时光倒流,回到过去,作为一个开发人员,你可以告诉自己在职业生涯初期应该读一本,你会选择哪本书呢?我希望这个书单列表内容丰富,可以涵盖很多东西.” 很多程序员响应,他们在推荐时也写下自己的评语.以前就有国内网友介绍这个程序员书单,不过都是推荐数 Top 10的书.其实除了前10本之外,推荐数前30左右的书籍都算经典,伯乐在线整理编译这个问答贴,同时摘译部分推荐人的评语.下面就按照各本书的推荐数排列. 1. <代码大全>史蒂夫·迈克康奈尔 推荐数:1684 “优秀的编程实践的百科全书,&l

一个合格的程序员应该读过哪些书

编者按:2008年8月4日,StackOverflow 网友 Bert F 发帖提问:哪本最具影响力的书,是每个程序员都应该读的? “如果能时光倒流,回到过去,作为一个开发人员,你可以告诉自己在职业生涯初期应该读一本,你会选择哪本书呢?我希望这个书单列表内容丰富,可以涵盖很多东西.” 很多程序员响应,他们在推荐时也写下自己的评语.以前就有国内网友介绍这个程序员书单,不过都是推荐数 Top 10的书. 其实除了前10本之外,推荐数前30左右的书籍都算经典,笔者整理编译这个问答贴,同时摘译部分推荐人

程序员必读书籍30本

一个合格的程序员应该读过哪些书 编者按:2008年8月4日,StackOverflow 网友 Bert F 发帖提问:哪本最具影响力的书,是每个程序员都应该读的? “如果能时光倒流,回到过去,作为一个开发人员,你可以告诉自己在职业生涯初期应该读一本, 你会选择哪本书呢?我希望这个书单列表内容丰富,可以涵盖很多东西.” 很多程序员响应,他们在推荐时也写下自己的评语. 以前就有国内网友介绍这个程序员书单,不过都是推荐数 Top 10的书. 其实除了前10本之外,推荐数前30左右的书籍都算经典,笔者整

【Unity】4.7 摄像机

分类:Unity.C#.VS2015 创建日期:2016-04-11 一.简介 摄像机(Camera)是为玩家捕捉并展示世界的一种设备.场景中至少需要有一台摄像机,也可以在一个场景中使用多台摄像机.这些摄像机可以设置为在屏幕的任何位置或只在某些部分按任何顺序进行渲染. 要将游戏呈现给玩家,相机是必不可少的.可以对相机进行自定义.脚本化或父子化,从而实现可以想到的任何效果.在拼图游戏中,可以让相机 (Camera) 处于静止状态,以看到拼图的全视图.在第一人称射击游戏中,可以将相机 (Camera

每个程序员都应读的书(转)

收藏,有时间,就读一读,有好处! 很多程序员响应,他们在推荐时也写下自己的评语.以前就有国内网友介绍这个程序员书单,不过都是推荐数 Top 10的书.其实除了前10本之外,推荐数前30左右的书籍都算经典,伯乐在线整理编译这个问答贴,同时摘译部分推荐人的评语.下面就按照各本书的推荐数排列. 1. <代码大全> 史蒂夫·迈克康奈尔 推荐数:1684 “优秀的编程实践的百科全书,<代码大全>注重个人技术,其中所有东西加起来,就是我们本能所说的“编写整洁的代码”.这本书有50页在谈论代码布