DirectX11笔记4:光照

按照书中的光照模型,光的类型分为3种:漫反射光,环境光,镜面光。

光源类型也是三种:平行光,点光,聚光灯。

其它需要的数据:材质,法线方向(光照角度)。

现在,先以平行光源为例,因为它最简单,不需要去计算距离,角度对于光线的影响:

先定义光源:

    //这里所有的定义只与光的颜色有关,就是定义光的颜色
    DirLight.Ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);//环境光
    DirLight.Diffuse = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);//漫反射光
    DirLight.Specular = XMFLOAT4(1.0f, 1.0f, 1.0f, 0.0f);//镜面光

1.环境光:

所谓环境光,可以认为有一种光充满了整个照射区域,无论在哪个位置,都可以被覆盖到。他的作用是用于模拟现实世界中的漫反射效果。

实现公式..    A=la?ma  ,解释,获得的环境光等于输入的环境光(Input Ambient 三维向量)逐项相乘材质的环境光系数(Material Ambient三维向量)

HLSL代码:    ambient = mat.Ambient * L.Ambient;

例子:光线(0.2,0.2,0.2)*材质(0.5,0.5,0.5) = (0.1,0.1,0.1)

也就是说,环境光只与材质,光本身有关,与角度无关。

(这里好郁闷。。为什么?表示规是逐项相乘。。。。我记得我们的教材中这个符号表示叉乘。。。而且向量有这种定义吗。。。以下所有的?都表示逐项相乘)

2.漫反射光:

很简单,光打在粗糙的表面上会有各个方向的均匀反射,因此无论在那个方向,我们看到的光的强度是一样的。

实现公式:cd = kd•ld ? md    解释:后一半和前面的环境光一样,材质与光线逐项相乘,前一个kd表示的是一个"角度",在书中7.1的法线定理与7.2中的兰伯特余弦定义有详细的解释,照射点的法线与朝向光线角度的余弦值。

代码与后面的镜面光一起。

3.镜面光(也有叫做高光的):

对于一些光滑的材质,光会形成镜面反射,这些光只有在一些特别的角度才可以看见。

实现公式:cs = ks•ls ? ms   这里的ks指的是观察方向与与反射的光线方向的夹角(角度为0时最强)

完全代码:

变量的定义:

//-------------------------------------------------------------------------------------
//定义光照结构
//----------------------------------------------------------------------------------------
struct DirectionalLight    ////平行光
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Direction;
    float pad;
};
//--------------------------------------------------------------------------------------
// 常量缓冲定义
//--------------------------------------------------------------------------------------
cbuffer ConstantBuffer : register(b0)    //世界,投影坐标转换
{
    matrix World;
    matrix View;
    matrix Projection;
}
cbuffer cbPerFrameLight                        //光线结构打包,暂时就一个平行光
{
    DirectionalLight gDirLight;    //平行光
    //PointLight gPointLight;        //点光
    //SpotLight gSpotLight;        //聚光灯
    float4 gEyePosW;            //眼睛坐标
};

//--------------------------------------------------------------------------------------
//其它结构定义
//--------------------------------------------------------------------------------------
struct Material
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular; // w = SpecPower
    float4 Reflect;
};

函数的实现:

//--------------------------------------------------------------------------------------
// 功能函数实现
//--------------------------------------------------------------------------------------
//实现平行光ComputeDirectionalLight(材质,平行光结构,反射点法线,指向眼睛的单位向量,返回1,返回2,返回3)
void ComputeDirectionalLight(
    Material mat, DirectionalLight L,
    float3 normal, float3 toEye,
    out float4 ambient,
    out float4 diffuse,
    out float4 spec)
{
    // 初始化输出的变量
    ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
    diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
    spec = float4(0.0f, 0.0f, 0.0f, 0.0f);

    // 光照矢量与光线的传播方向相反
    float3 lightVec = -L.Direction;

        // 添加环境光
        ambient = mat.Ambient * L.Ambient;

    // 添加漫反射和镜面光,注意,保证normal,lightVec是单位向量(归一化)
    float diffuseFactor = dot(lightVec, normal);//计算cosA

    // Flatten避免动态分支
    [flatten]
    if (diffuseFactor > 0.0f)    //如果角度的余弦值大于0,
    {
        float3 v = reflect(-lightVec, normal);//reflect通过入射光与法线计算反射向量
            float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);//pow用于计算材质光的线反射度

        diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
        spec = specFactor * mat.Specular * L.Specular;
    }
}

相比于原书的代码,我做了更多的注释,但还是要解释一遍:

已经叙述过的环境光计算:ambient = mat.Ambient * L.Ambient;

这里面光线的矢量方向是光线入射方向的反向,也就是反射点指向光源的方向,平行光直接反向就可以了:float3 lightVec = -L.Direction;

计算反射的法线与光线矢量的夹角的余弦值:float diffuseFactor = dot(lightVec, normal),这里面的normal是法线,lightVec上面计算了,他们都是单位向量,只有这样的点乘为cos值,还有一点,关于法线的计算方式可以看书中的7.2节。

然后是一个if语句,关于HLSL中的支持控制语句看msdn的介绍:

https://msdn.microsoft.com/en-us/library/bb509600.aspx

这里面的Flatten与branch的解释我是完全没明白。。。。。再说吧。。。。。。

使用入射光与法线向量计算反射光:float3 v = reflect(-lightVec, normal);

计算反射光与指向眼睛的向量(观察向量,指向观察点)的余弦值dot(v, toEye),判断它是否大于0:max(dot(v, toEye))

最后使用pow对材质进行分析,这是为了用于区分不同材质的反射能力不同。

这一节比较简单,不给代码了。其它的2种光源差不多,不再详细分析了。。。。。。

好麻烦啊。

追加:关于flatten与branch

https://msdn.microsoft.com/en-us/library/windows/desktop/bb509610

http://www.gamedev.net/topic/650462-branch-and-flatten-attributes-in-sm-5/

我的理解:GPU是尽量避免分支的,因此程序编译的时候会采用一种启发式的智能优化方法去摘除分支,这是为了同个程序在不同分支的情况下都可以有相同的运行时间,避免时间的浪费?但是我们知道有时候一些分支是必须的,因此他提供了branch与flatten来让我们判断是否使用动态分支。书中例子使用了flatten避免了动态分支。

------以上为我看文档与评论得出的结论,自己没有实际的方法去验证。

时间: 2024-08-08 05:34:53

DirectX11笔记4:光照的相关文章

DirectX11笔记3:基本绘图,渲染,绘制一个旋转方形

上一节的笔记自己写的十分糟糕,那个程序也写的十分糟糕.........如果真的有人看的话,说声抱歉. 这一节主要是记录一个旋转的正方形的制作过程,先说好:以下所有内容请配合上传了的代码食用..........如果真的有人看的话. 首先,先大概介绍一下绘制一个图形的基本流程: 一.创建基本的D3D对象: 1.使用D3D11CreateDeviceAndSwapChain创建D3D设备对象与交换链. 2.使用CreateRenderTargetView创建后一个绘制缓冲区. 3.如果需要,创建模板与

2.传感器学习笔记之光照传感器

这节课我们来学习安卓中的光照传感器,已经会的请绕过此篇. 安卓系统有个自动调整屏幕亮度的功能. 它会检测手机周围环境的光照强度, 然后对手机屏幕的亮度进行相应地调整, 以此保证不管是在强光还是弱光下,手机屏幕都能够看得清. 说的太多理论有点虚,这里直接实战实用光照传感器做个项目. 本课目标: 编写一个简易的光照探测器程序,使手机可以检测到周围环境的光照强度变化. 效果图: 说明: 灯光照到上面数值会发生改变. 友情提示: 模拟器无法感知光照强度,因此建议在真实手机上运行. 1.首先我们新建一个安

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记8——光照与材质

第14章 绘制出质感的世界--光照与材质 1. 光照与光源 在Direct3D中的光源类型和光照类型是不同的两个概念,光照模型描述的是光线的反射特征,而光源类型主要强调的是能够产生这些光照模型的方式以及光线的位置,方向,强度等特征. 四大光照类型 环境光:基于整个自然界环境的整体亮度,称为环境光或者背景光,没有位置或者方向上的特征,只有一个颜色亮度值,不会衰减,在所有方向和所有物体表面上投射的环境光的数量是恒定不变的(有点像我们白天的自然光).在Direc3D中设置环境光可以直接使用setRen

DirectX11笔记7:模板Stenciling

跟以前的一样,对于模板缓冲区,也是声明定义一系列的结构体,并在渲染的时候 打开/关闭 他们 在使用CreateRenderTargetView创建视图之后,声明定义深度模板缓冲区: ID3D11Texture2D *depthStencilBuffer(NULL); //测试多重采样抗锯齿等级 UINT x4MsaaQuality; dev->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, 4, &x4MsaaQuality

DirectX11笔记6:混合blend

所谓混合,目的就是解决一些需要叠加处理的材质问题,书中有个很好的例子:雾. 当然也有其它的用法,比如把2个不同材质叠加,产生各种神奇的效果(参见9.5.2节). 混合的代码操作: 按照一贯的逻辑,我们在D3D里创建一个东西都是要填一大堆结构体,然后使用一个函数初始化,再在渲染步骤中进行进一步处理.这里一样.... 使用CreateBlendState初始化对混合的描述: virtual HRESULT STDMETHODCALLTYPE CreateBlendState( /* [annotat

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",

Directx11学习笔记【二十二】 用高度图实现地形

本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5827714.html 在前面我们曾经实现过简单的地形(Directx11学习笔记[十三] 实现一个简单地形),只不过原来使用一个固定的函数获得地形高度,这样跟真实的地形差距比较大.接下来让我们学习使用高度图来进行三维地形模拟. 1.高度图 高度图其实就是一组连续的数组,这个数组中的元素与地形网格中的顶点一一对应,且每一个元素都指定了地形网格的某个顶点的高度值.高度

Directx11学习笔记【二】 将HelloWin封装成类

我们把上一个教程的代码封装到一个类中来方便以后的使用. 首先新建一个空工程叫做MyHelloWin,添加一个main.cpp文件,然后新建一个类叫做MyWindow,将于窗体有关的操作封装到里面 MyWindow.h文件 1 /************************************************************************ 2 Directx11学习笔记[2] 将HelloWin封装成类 3 2016.01 by zhangbaochong 4 /

Directx11学习笔记【十】 画一个简单的三角形

本篇笔记要实现的是在屏幕上渲染出一个三角形,重点要学习的是渲染一个几何体的流程方式. 为了渲染几何图形,需要一个顶点缓存和一个描述顶点布局的输入层,还有着色器(主要是顶点着色器和像素着色器),下面来看看具体Demo的实现. 新建一个Win32项目 ,新建一个类我们叫做TriangleDemo,继承自前面教程我们实现的基类Dx11DemoBase. TriangleDemo.h头文件 #pragma once #include "Dx11DemoBase.h" class Triangl