Directx11学习笔记【十六】 光照模型的实现
本文由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++程序中平行光的定义:
struct DirectionalLight
{
DirectionalLight() { ZeroMemory(this, sizeof(this)); } XMFLOAT4 Ambient;//环境光
XMFLOAT4 Diffuse;//漫反射光
XMFLOAT4 Specular;//高光
XMFLOAT3 Direction;//光照方向
float Pad; // Pad the last float so we can set an array of lights if we wanted.用于与HLSL中“4D向量”对齐规则匹配
};
4.2 点光源
c++程序中点光源的定义:
struct PointLight
{
PointLight() { ZeroMemory(this, sizeof(this)); } XMFLOAT4 Ambient;
XMFLOAT4 Diffuse;
XMFLOAT4 Specular; // Packed into 4D vector: (Position, Range)
XMFLOAT3 Position;//光源位置
float Range; //光照范围 // Packed into 4D vector: (A0, A1, A2, Pad)
XMFLOAT3 Att; //衰减系数
float Pad; // Pad the last float so we can set an array of lights if we wanted.
};
4.3 聚光灯
c++程序中聚光灯定义:
struct SpotLight
{
SpotLight() { ZeroMemory(this, sizeof(this)); } XMFLOAT4 Ambient;
XMFLOAT4 Diffuse;
XMFLOAT4 Specular; // Packed into 4D vector: (Position, Range)
XMFLOAT3 Position;//光照位置
float Range; //光照范围 // Packed into 4D vector: (Direction, Spot)
XMFLOAT3 Direction;//光照方向
float Spot; //光照强度系数 // Packed into 4D vector: (Att, Pad)
XMFLOAT3 Att; //衰减系数
float Pad; // Pad the last float so we can set an array of lights if we wanted.
};
4.4 HLSL中三种光源的定义
struct DirectionalLight
{
float4 Ambient;
float4 Diffuse;
float4 Specular;
float3 Direction;
float pad;
}; struct PointLight
{
float4 Ambient;
float4 Diffuse;
float4 Specular; float3 Position;
float Range; float3 Att;
float pad;
}; struct SpotLight
{
float4 Ambient;
float4 Diffuse;
float4 Specular; float3 Position;
float Range; float3 Direction;
float Spot; float3 Att;
float pad;
};
4.5 材质
材质同样有环境光、漫反射光和高光三种成分,此外还有一个材质的镜面反射系数即表示光滑程度。
c++程序中定义:
struct Material
{
Material() { ZeroMemory(this, sizeof(this)); } XMFLOAT4 Ambient;
XMFLOAT4 Diffuse;
XMFLOAT4 Specular;//w表示高光强度
XMFLOAT4 Reflect;
};
HLSL定义:
struct Material
{
float4 Ambient;
float4 Diffuse;
float4 Specular;
float4 Reflect;
};
5. 光照计算
光照计算无疑是最重要的部分,这一部分在HLSL中实现。
5.1 平行光
void ComputeDirectionalLight(Material mat, //材质
DirectionalLight L, //平行光
float3 normal, //顶点法线
float3 toEye, //顶点到眼睛的向量
out float4 ambient, //计算结果:环境光
out float4 diffuse, //计算结果:漫反射光
out float4 spec) //计算结果:高光
{
// 结果初始化为0
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; // 计算漫反射系数
//光线、法线方向归一化 float diffuseFactor = dot(lightVec, normal); // 顶点背向光源不再计算
[flatten]
if (diffuseFactor > 0.0f)
{ float3 v = reflect(-lightVec, normal);
float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
//计算漫反射光
diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
//计算高光
spec = specFactor * mat.Specular * L.Specular;
}
}
5.2 点光源
void ComputePointLight(Material mat, //材质
PointLight L, //点光源
float3 pos, //顶点位置
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.Position - pos; //顶点到光源距离
float d = length(lightVec); //超过范围不再计算
if (d > L.Range)
return; //归一化光照方向
lightVec /= d; //计算环境光
ambient = mat.Ambient * L.Ambient; //漫反射系数
float diffuseFactor = dot(lightVec, normal); [flatten]
if (diffuseFactor > 0.0f)
{
float3 v = reflect(-lightVec, normal);
float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
//计算漫反射光
diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
//计算高光
spec = specFactor * mat.Specular * L.Specular;
} // 计算衰减
float att = 1.0f / dot(L.Att, float3(1.0f, d, d*d)); diffuse *= att;
spec *= att;
}
5.3 聚光灯
void ComputeSpotLight(Material mat, //材质
SpotLight L, //聚光灯
float3 pos, //顶点位置
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.Position - pos; //顶点到光源距离
float d = length(lightVec); //距离大于光照方向不再计算
if (d > L.Range)
return; //归一化光照方向
lightVec /= d; //计算环境光
ambient = mat.Ambient * L.Ambient; //计算漫反射系数
float diffuseFactor = dot(lightVec, normal); [flatten]
if (diffuseFactor > 0.0f)
{
float3 v = reflect(-lightVec, normal);
float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
//漫反射光
diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
//高光
spec = specFactor * mat.Specular * L.Specular;
} //聚光衰减系数
float spot = pow(max(dot(-lightVec, L.Direction), 0.0f), L.Spot); //衰减系数
float att = spot / dot(L.Att, float3(1.0f, d, d*d)); ambient *= spot;
diffuse *= att;
spec *= att;
}
6.程序中使用的shader文件
#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); //初始化颜色值全部为0
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; //每个光源计算后将ADS更新到最终结果中
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; //最终颜色透明度使用漫反射光的
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()));
}
}
7.程序运行结果
由于代码比较多,这里就不给出了,有兴趣的可以下载看看注释也比较详细
源码下载:http://files.cnblogs.com/files/zhangbaochong/LightDemo.zip
Directx11学习笔记【十六】 光照模型的实现的更多相关文章
- python3.4学习笔记(十六) windows下面安装easy_install和pip教程
python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...
- (C/C++学习笔记) 十六. 预处理
十六. 预处理 ● 关键字typeof 作用: 为一个已有的数据类型起一个或多个别名(alias), 从而增加了代码的可读性. typedef known_type_name new_type_nam ...
- JavaScript权威设计--CSS(简要学习笔记十六)
1.Document的一些特殊属性 document.lastModified document.URL document.title document.referrer document.domai ...
- MySQL学习笔记十六:锁机制
1.数据库锁就是为了保证数据库数据的一致性在一个共享资源被并发访问时使得数据访问顺序化的机制.MySQL数据库的锁机制比较独特,支持不同的存储引擎使用不同的锁机制. 2.MySQL使用了三种类型的锁机 ...
- python 学习笔记十六 django深入学习一 路由系统,模板,admin,数据库操作
django 请求流程图 django 路由系统 在django中我们可以通过定义urls,让不同的url路由到不同的处理函数 from . import views urlpatterns = [ ...
- SharpGL学习笔记(十六) 多重纹理映射
多重纹理就把多张贴图隔和在一起.比如下面示例中,一个表现砖墙的纹理,配合一个表现聚光灯效果的灰度图,就形成了砖墙被一个聚光灯照亮的效果,这便是所谓的光照贴图技术. 多重纹理只在OpenGL扩展库中才提 ...
- yii2源码学习笔记(十六)
Module类的最后代码 /** * Registers sub-modules in the current module. * 注册子模块到当前模块 * Each sub-module shoul ...
- Swift学习笔记十六:协议
Protocol(协议)用于统一方法和属性的名称,而不实现不论什么功能. 协议可以被类.枚举.结构体实现.满足协议要求的类,枚举,结构体被称为协议的遵循者. 遵循者须要提供协议指定的成员,如属性,方法 ...
- PHP学习笔记十六【方法】
<?php //给一个函数传递基本数据类型 $a=90; $b=90.8; $c=true; $d="hello world"; function test1($a,$b,$ ...
- Directx11学习笔记【六】 基本的数学知识----矩阵篇
参考dx11龙书 Chapter2 matrix algebra(矩阵代数) 关于矩阵的一些基本概念定理(例如矩阵加减乘法,逆矩阵,伴随矩阵,转置矩阵等)可以参考维基百科 https://zh.wik ...
随机推荐
- nagios二次开发(五岁以下儿童)---nagios和nagiosql关系
基于nagios和nagiosql理解.这将是这两个梳理比较粗糙的简单关系,有关详细信息,请参阅下面的图如: 从上面的关系图中能够看出,nagios与nagiosql共享了主机.主机组.服务 ...
- Linux 核心阅读工具vim+ctags+cscope+taglist
今天.介绍vim+ctags+cscope+taglist的内核阅读配置. 当使用过之后,我相信大部分人都会舍弃之前的Eclipse(我就是活生生的一个样例).我们先来看看实现的界面是怎么样的: 我们 ...
- 使用RouteDebugger对MVC路由进行调试
在Asp.Net MVC程序中,路由是MVC程序的入口,每一个Http请求都要经过路由计算,然后匹配到相应的Controller和Action.通常我们的路由都会注册在Global.asax.cs文件 ...
- c# Unicode字符串的解码
前两天工作中遇到个奇怪的问题,一个unicode字符串(即“\uXXXX”形式)变量,调用HttpUtility.UrlDecode解码过后,还是原样,要么就是乱码状态.无奈之下只能自己写一个解码函数 ...
- Unity3D游戏开发从零单排(四) - 制作一个iOS游戏
提要 此篇是一个国外教程的翻译,尽管有点老,可是适合新手入门. 自己去写代码.debug,布置场景,能够收获到非常多.游戏邦上已经有前面两部分的译文,这里翻译的是游戏的最后一个部分. 欢迎回来 在第一 ...
- 在IIS上发布一个WebService,再发布一个网站调用这个WebService(实例)
首先描述一下先决条件:IIS可用,VS2005可用. 好,现在开始: 首先写一个WebService并把它发布到IIS上: 在IIS上的默认网站下新建一个“虚拟目录”,取名为“webservice1” ...
- zoj3209(DLX)
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16234 题意:给p张小纸片, 问能不能选出尽量少的一部分或全部数量 ...
- Java8高中并发
Java8中学并发 本文翻译自:http://jaxenter.com/lean-concurrency-in-java-8-49924.html 转载请注明出处:http://blog.csdn.n ...
- 重构ConditionHelper
在上一篇日志写了ConditionHelper的具体实现方法和它的用法,但那时只是把心里所想的一个思路用代码记录下来,而没有经过实践检 验的,难免存在一些问题,但在最近的一个新项目中用到Conditi ...
- Luci - UCI (Unified Configuration Interface)
参考: http://wiki.openwrt.org/doc/techref/uc http://luci.subsignal.org/api/luci/modules/luci.model.uci ...