本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5579289.html

  在之前的场景绘制中我们都是给每个顶点指定了单一颜色 ,然后由系统插值计算各个部分颜色,最终显示出来。接下来我们将学习dx11中比较有意思的一部分——光照,通过光照和材质的相互作用来实现更真实的场景。

1. 光照

1.1 光照效果

  简单举个例子,看龙书中的一张图:

  

  a图没加光照看起来像一个2D图形,而加了光照的b图则看起来像一个3D图形。由此可见,光照在3D渲染方面是尤为重要的,通常借助光照可以让场景显得更加真实。

1.2 材质

  材质可以说是决定光如何作用在物体表面的一种属性。例如,光在物体表面反射和吸收的颜色,它的反射率、透明度、光滑程度等属性组成了一个物体表面的材质。在我们下面的示例一般只考虑光的反射和吸收颜色以及光滑程度用来计算全反射,暂不考虑其它因素。

2.法线

2.1面法线(face normal)

  什么是面法线呢?A face normal is a vector that describesthe direction a polygon is facing。

  

  计算面法线也很简单,在面内找到三点,取得两个向量作×积然后单位化即可。

  

2.2 顶点法线(vertex normal)

     在directx中我们需要知道顶点的法线,法线决定了光照射平面的角度。光线会被应用到每个顶点,并且根据面法线和光照方向的点积去调整光线颜色的强度。那么怎么计算顶点法线呢?下面的图介绍的很清楚了:

   

  

2.3 法线变换

     在一个顶点进行空间变换时,法线通常也需要进行变换。但是注意:顶点和法线的变换矩阵并不相同!

    

   对于一个顶点的变换矩阵A,其对应的法线变换时A的逆矩阵的转置。

2.4 朗伯余弦定律

   

     当一个面元的辐射亮度和光亮度在其表面上半球的所有方向相等时,并符合I(θ) = INcosθ时称为朗伯余弦定律。

     I(θ)----面元在θ角(与表面法线夹角)方向及其法线方向的辐射强度

     IN----面元在θ角方向及其法线方向的发光亮度

3. 光照计算处理的三部分

    光照计算处理共有三个部分:环境光(ambient)、漫反射光(diffuse)以及全反射光(又称高光,specular)。

3.1 环境光(ambient)

   在现实当中,光照是一个很复杂的物理现象。一个物体所接受的光,除了直接来自光源的部分外,还包括光源经过环境中其他各个物体的反射而来的部分。而在图形学中,我们默认的光照模型为局部光模型,即一个顶点的光照计算只跟该点信息与光源信息有关,而不考虑环境中其他物体的影响,比如阴影等。

3.2 漫反射光(diffuse)

   光照射在物体表面后,其反射光沿随机方向均匀的分布,即"漫反射”。反射光的强度与光照方向与表面法线的夹角theta相关,满足比例关系:I = Io * cos(theta)。由于反射光方向随机,因此该部分的计算与观察点无关,而只与光线方向与法线相关。

3.3 全反射光(specular)

   光线照射在光滑物体表面后,在特定方向上会有很强的反射,即发生全反射。全反射光主要集中在一个近似圆锥角的范围内。如下图所示:

   

n为法线,l为光线入射方向,r为全反射方向,E为观察点,因此v为视角方向。全反射光进入眼睛的强度与v和r的角度theta有关,随着该角度增大,全反射强度下降,其下降辐度与物体表面光滑程序相关。因此,对于该部分光的计算,除了需要光线方向、法线等信息外,还与观察点的位置有很大关系。具体计算公式在本文后面会详细给出。

4. 光源模型

4.1 平行光

平行光是最简单的一种光照模型,光照方向不变而且光照强度不随空间位置改变,可以用平行光来模拟日常生活中的太阳光。

   c++程序中平行光的定义: 

  1. struct DirectionalLight
  2. {
  3. DirectionalLight() { ZeroMemory(this, sizeof(this)); }
  4.  
  5. XMFLOAT4 Ambient;//环境光
  6. XMFLOAT4 Diffuse;//漫反射光
  7. XMFLOAT4 Specular;//高光
  8. XMFLOAT3 Direction;//光照方向
  9. float Pad; // Pad the last float so we can set an array of lights if we wanted.用于与HLSL中“4D向量”对齐规则匹配
  10. };

4.2 点光源

   c++程序中点光源的定义:

  1. struct PointLight
  2. {
  3. PointLight() { ZeroMemory(this, sizeof(this)); }
  4.  
  5. XMFLOAT4 Ambient;
  6. XMFLOAT4 Diffuse;
  7. XMFLOAT4 Specular;
  8.  
  9. // Packed into 4D vector: (Position, Range)
  10. XMFLOAT3 Position;//光源位置
  11. float Range; //光照范围
  12.  
  13. // Packed into 4D vector: (A0, A1, A2, Pad)
  14. XMFLOAT3 Att; //衰减系数
  15. float Pad; // Pad the last float so we can set an array of lights if we wanted.
  16. };

4.3 聚光灯

c++程序中聚光灯定义:

  1. struct SpotLight
  2. {
  3. SpotLight() { ZeroMemory(this, sizeof(this)); }
  4.  
  5. XMFLOAT4 Ambient;
  6. XMFLOAT4 Diffuse;
  7. XMFLOAT4 Specular;
  8.  
  9. // Packed into 4D vector: (Position, Range)
  10. XMFLOAT3 Position;//光照位置
  11. float Range; //光照范围
  12.  
  13. // Packed into 4D vector: (Direction, Spot)
  14. XMFLOAT3 Direction;//光照方向
  15. float Spot; //光照强度系数
  16.  
  17. // Packed into 4D vector: (Att, Pad)
  18. XMFLOAT3 Att; //衰减系数
  19. float Pad; // Pad the last float so we can set an array of lights if we wanted.
  20. };

4.4 HLSL中三种光源的定义

  1. struct DirectionalLight
  2. {
  3. float4 Ambient;
  4. float4 Diffuse;
  5. float4 Specular;
  6. float3 Direction;
  7. float pad;
  8. };
  9.  
  10. struct PointLight
  11. {
  12. float4 Ambient;
  13. float4 Diffuse;
  14. float4 Specular;
  15.  
  16. float3 Position;
  17. float Range;
  18.  
  19. float3 Att;
  20. float pad;
  21. };
  22.  
  23. struct SpotLight
  24. {
  25. float4 Ambient;
  26. float4 Diffuse;
  27. float4 Specular;
  28.  
  29. float3 Position;
  30. float Range;
  31.  
  32. float3 Direction;
  33. float Spot;
  34.  
  35. float3 Att;
  36. float pad;
  37. };

4.5 材质

    材质同样有环境光、漫反射光和高光三种成分,此外还有一个材质的镜面反射系数即表示光滑程度。

c++程序中定义:

  1. struct Material
  2. {
  3. Material() { ZeroMemory(this, sizeof(this)); }
  4.  
  5. XMFLOAT4 Ambient;
  6. XMFLOAT4 Diffuse;
  7. XMFLOAT4 Specular;//w表示高光强度
  8. XMFLOAT4 Reflect;
  9. };

HLSL定义:

  1. struct Material
  2. {
  3. float4 Ambient;
  4. float4 Diffuse;
  5. float4 Specular;
  6. float4 Reflect;
  7. };

5. 光照计算

  光照计算无疑是最重要的部分,这一部分在HLSL中实现。

5.1 平行光

  1. void ComputeDirectionalLight(Material mat, //材质
  2. DirectionalLight L, //平行光
  3. float3 normal, //顶点法线
  4. float3 toEye, //顶点到眼睛的向量
  5. out float4 ambient, //计算结果:环境光
  6. out float4 diffuse, //计算结果:漫反射光
  7. out float4 spec) //计算结果:高光
  8. {
  9. // 结果初始化为0
  10. ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
  11. diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
  12. spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
  13.  
  14. // 光线方向
  15. float3 lightVec = -L.Direction;
  16.  
  17. // 环境光直接计算
  18. ambient = mat.Ambient * L.Ambient;
  19.  
  20. // 计算漫反射系数
  21. //光线、法线方向归一化
  22.  
  23. float diffuseFactor = dot(lightVec, normal);
  24.  
  25. // 顶点背向光源不再计算
  26. [flatten]
  27. if (diffuseFactor > 0.0f)
  28. {
  29.  
  30. float3 v = reflect(-lightVec, normal);
  31. float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
  32. //计算漫反射光
  33. diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
  34. //计算高光
  35. spec = specFactor * mat.Specular * L.Specular;
  36. }
  37. }

5.2 点光源

  1. void ComputePointLight(Material mat, //材质
  2. PointLight L, //点光源
  3. float3 pos, //顶点位置
  4. float3 normal, //顶点法线
  5. float3 toEye, //顶点到眼睛的向量
  6. out float4 ambient, //计算结果:环境光
  7. out float4 diffuse, //计算结果:漫反射光
  8. out float4 spec) //计算结果:高光
  9. {
  10. ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
  11. diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
  12. spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
  13.  
  14. //光照方向:顶点到光源
  15. float3 lightVec = L.Position - pos;
  16.  
  17. //顶点到光源距离
  18. float d = length(lightVec);
  19.  
  20. //超过范围不再计算
  21. if (d > L.Range)
  22. return;
  23.  
  24. //归一化光照方向
  25. lightVec /= d;
  26.  
  27. //计算环境光
  28. ambient = mat.Ambient * L.Ambient;
  29.  
  30. //漫反射系数
  31. float diffuseFactor = dot(lightVec, normal);
  32.  
  33. [flatten]
  34. if (diffuseFactor > 0.0f)
  35. {
  36. float3 v = reflect(-lightVec, normal);
  37. float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
  38. //计算漫反射光
  39. diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
  40. //计算高光
  41. spec = specFactor * mat.Specular * L.Specular;
  42. }
  43.  
  44. // 计算衰减
  45. float att = 1.0f / dot(L.Att, float3(1.0f, d, d*d));
  46.  
  47. diffuse *= att;
  48. spec *= att;
  49. }

5.3 聚光灯

  1. void ComputeSpotLight(Material mat, //材质
  2. SpotLight L, //聚光灯
  3. float3 pos, //顶点位置
  4. float3 normal, //顶点法线
  5. float3 toEye, //顶点到眼睛向量
  6. out float4 ambient, //计算结果:环境光
  7. out float4 diffuse, //计算结果:漫反射光
  8. out float4 spec) //计算结果:高光
  9. {
  10. //初始化结果
  11. ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
  12. diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
  13. spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
  14.  
  15. //光照方向:顶点到光源
  16. float3 lightVec = L.Position - pos;
  17.  
  18. //顶点到光源距离
  19. float d = length(lightVec);
  20.  
  21. //距离大于光照方向不再计算
  22. if (d > L.Range)
  23. return;
  24.  
  25. //归一化光照方向
  26. lightVec /= d;
  27.  
  28. //计算环境光
  29. ambient = mat.Ambient * L.Ambient;
  30.  
  31. //计算漫反射系数
  32. float diffuseFactor = dot(lightVec, normal);
  33.  
  34. [flatten]
  35. if (diffuseFactor > 0.0f)
  36. {
  37. float3 v = reflect(-lightVec, normal);
  38. float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
  39. //漫反射光
  40. diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
  41. //高光
  42. spec = specFactor * mat.Specular * L.Specular;
  43. }
  44.  
  45. //聚光衰减系数
  46. float spot = pow(max(dot(-lightVec, L.Direction), 0.0f), L.Spot);
  47.  
  48. //衰减系数
  49. float att = spot / dot(L.Att, float3(1.0f, d, d*d));
  50.  
  51. ambient *= spot;
  52. diffuse *= att;
  53. spec *= att;
  54. }

6.程序中使用的shader文件

  1. #include "LightHelper.fx"
  2.  
  3. cbuffer cbPerFrame
  4. {
  5. DirectionalLight gDirLight;
  6. PointLight gPointLight;
  7. SpotLight gSpotLight;
  8. float3 gEyePosW; //观察点
  9. };
  10.  
  11. cbuffer cbPerObject
  12. {
  13. float4x4 gWorld;
  14. float4x4 gWorldInvTranspose;//世界矩阵的逆矩阵的转置
  15. float4x4 gWorldViewProj;
  16. Material gMaterial;
  17. };
  18.  
  19. struct VertexIn
  20. {
  21. float3 PosL : POSITION; //顶点坐标
  22. float3 NormalL : NORMAL; //顶点法线
  23. };
  24.  
  25. struct VertexOut
  26. {
  27. float4 PosH : SV_POSITION; //投影后的坐标
  28. float3 PosW : POSITION; //世界变换后的坐标
  29. float3 NormalW : NORMAL; //世界变换后的顶点法线
  30. };
  31.  
  32. VertexOut VS(VertexIn vin)
  33. {
  34. VertexOut vout;
  35.  
  36. vout.PosW = mul(float4(vin.PosL, 1.0f), gWorld).xyz;
  37. vout.NormalW = mul(vin.NormalL, (float3x3)gWorldInvTranspose);
  38.  
  39. vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj);
  40.  
  41. return vout;
  42. }
  43.  
  44. float4 PS(VertexOut pin) : SV_Target
  45. {
  46. //插值运算有可能使法线不再单位化,重新单位化法线
  47. pin.NormalW = normalize(pin.NormalW);
  48.  
  49. //顶点到观察点向量,归一化
  50. float3 toEyeW = normalize(gEyePosW - pin.PosW);
  51.  
  52. //初始化颜色值全部为0
  53. float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
  54. float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
  55. float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
  56.  
  57. //每个光源计算后得到的环境光、漫反射光、高光
  58. float4 A, D, S;
  59.  
  60. //每个光源计算后将ADS更新到最终结果中
  61. ComputeDirectionalLight(gMaterial, gDirLight, pin.NormalW, toEyeW, A, D, S);
  62. ambient += A;
  63. diffuse += D;
  64. spec += S;
  65.  
  66. ComputePointLight(gMaterial, gPointLight, pin.PosW, pin.NormalW, toEyeW, A, D, S);
  67. ambient += A;
  68. diffuse += D;
  69. spec += S;
  70.  
  71. ComputeSpotLight(gMaterial, gSpotLight, pin.PosW, pin.NormalW, toEyeW, A, D, S);
  72. ambient += A;
  73. diffuse += D;
  74. spec += S;
  75.  
  76. float4 litColor = ambient + diffuse + spec;
  77.  
  78. //最终颜色透明度使用漫反射光的
  79. litColor.a = gMaterial.diffuse.a;
  80.  
  81. return litColor;
  82. }
  83.  
  84. technique11 LightTech
  85. {
  86. pass P0
  87. {
  88. SetVertexShader(CompileShader(vs_5_0, VS()));
  89. SetGeometryShader(NULL);
  90. SetPixelShader(CompileShader(ps_5_0, PS()));
  91. }
  92. }

7.程序运行结果

由于代码比较多,这里就不给出了,有兴趣的可以下载看看注释也比较详细

源码下载:http://files.cnblogs.com/files/zhangbaochong/LightDemo.zip

Directx11学习笔记【十六】 光照模型的实现的更多相关文章

  1. python3.4学习笔记(十六) windows下面安装easy_install和pip教程

    python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...

  2. (C/C++学习笔记) 十六. 预处理

    十六. 预处理 ● 关键字typeof 作用: 为一个已有的数据类型起一个或多个别名(alias), 从而增加了代码的可读性. typedef known_type_name new_type_nam ...

  3. JavaScript权威设计--CSS(简要学习笔记十六)

    1.Document的一些特殊属性 document.lastModified document.URL document.title document.referrer document.domai ...

  4. MySQL学习笔记十六:锁机制

    1.数据库锁就是为了保证数据库数据的一致性在一个共享资源被并发访问时使得数据访问顺序化的机制.MySQL数据库的锁机制比较独特,支持不同的存储引擎使用不同的锁机制. 2.MySQL使用了三种类型的锁机 ...

  5. python 学习笔记十六 django深入学习一 路由系统,模板,admin,数据库操作

    django 请求流程图 django 路由系统 在django中我们可以通过定义urls,让不同的url路由到不同的处理函数 from . import views urlpatterns = [ ...

  6. SharpGL学习笔记(十六) 多重纹理映射

    多重纹理就把多张贴图隔和在一起.比如下面示例中,一个表现砖墙的纹理,配合一个表现聚光灯效果的灰度图,就形成了砖墙被一个聚光灯照亮的效果,这便是所谓的光照贴图技术. 多重纹理只在OpenGL扩展库中才提 ...

  7. yii2源码学习笔记(十六)

    Module类的最后代码 /** * Registers sub-modules in the current module. * 注册子模块到当前模块 * Each sub-module shoul ...

  8. Swift学习笔记十六:协议

    Protocol(协议)用于统一方法和属性的名称,而不实现不论什么功能. 协议可以被类.枚举.结构体实现.满足协议要求的类,枚举,结构体被称为协议的遵循者. 遵循者须要提供协议指定的成员,如属性,方法 ...

  9. PHP学习笔记十六【方法】

    <?php //给一个函数传递基本数据类型 $a=90; $b=90.8; $c=true; $d="hello world"; function test1($a,$b,$ ...

  10. Directx11学习笔记【六】 基本的数学知识----矩阵篇

    参考dx11龙书 Chapter2 matrix algebra(矩阵代数) 关于矩阵的一些基本概念定理(例如矩阵加减乘法,逆矩阵,伴随矩阵,转置矩阵等)可以参考维基百科 https://zh.wik ...

随机推荐

  1. 智能手机的工业控制应用方案——SimpleWiFi在工业控制领域应用

    智能手机的工业控制应用方案——SimpleWiFi在工业控制领域应用    先上图: 现在的智能控制都是基于微控制器,随着智能的手持终端的普及,基于智能终端的控制就会越来越普遍. WIFI便是其中的一 ...

  2. oschina 建站系统

    建站系统 分类网站程序(9) 众筹平台(2) 团购网站系统(14) 开源轻博客系统(8) 开源博客系统(279) 视频网站系统(9) 开源微博工具(93) 论坛系统BBS(129) 建站系统CMS(5 ...

  3. Ubuntu Manpage: ajaxterm - Web based terminal written in python

    Ubuntu Manpage: ajaxterm - Web based terminal written in python hardy (1) ajaxterm.1.gz Provided by: ...

  4. c4Droid

    c4可以让用c/c++写的源码打包成apk安装包,支持Console.SDL.Qt. NativeActivity 等一系列扩展库,可以用来写软件,也可以用来写游戏,是手机端练习c/c++的神器.c4 ...

  5. iBeacon怎样工作

    原文地址 iBeacons iBeacons近期是一个趋势的话题,它们同意室内定位,让你的电话知道你在基站的范围.这个能有很多应用:在停车场帮你找到你的车,零售商通过优惠券和基于位置的特别优惠,以至很 ...

  6. PPTP和L2TP的区别

    PPTP是点到点的隧道协议,服务器端使用TCP 的1723端口,同时使用GRE协议,加密上使用MPPE.位于NAT后的客户端连接会有问题. L2TP是二层隧道VPN,使用IPsec 进行加密,服务器端 ...

  7. PHP:小数位计算

    本文提供了两种方法,分数的方法成为字符串.然后,"."为了拦截.跟.子长后.另一个是关于小数*10的N钍.实例10的8再次钍8取余次.然后继续10余.取决于10结果的余数是不0. ...

  8. An Overview of Complex Event Processing2

    An Overview of Complex Event Processing 翻译前言:感觉作者有点夸夸其谈兼絮絮叨叨,但文章还是很有用的.原文<An Overview of Complex ...

  9. Java线程中断的本质深入理解(转)

    一.Java中断的现象 首先,看看Thread类里的几个方法: public static boolean interrupted 测试当前线程是否已经中断.线程的中断状态 由该方法清除.换句话说,如 ...

  10. .NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper

    .NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper 參考演示样例代码,例如以下所看到的: /// <summary> /// MySql ...