3D图形学编程基础-基于Direct3D11-学习记录(二)光照模型的实现
来源:互联网 发布:linux查看服务列表 编辑:程序博客网 时间:2024/05/01 01:10
为了让绘制出的物体更接近真实世界,此章引入光照,光照章节不少都是概念论述以及少量的计算方式
和光照有关几个小概念,1,光照:方向,强度,衰弱 2.物体材质:光滑度,折射率,反射率和发光度等;3.顶点法线:我们处理物体被光照是按顶点处理的(绘制物体是按顶点绘制的)顶点法线求法:取所有共享此顶点的面的法线的平均值
在directx中我们需要知道顶点的法线,法线决定了光照射平面的角度。光线会被应用到每个顶点,并且根据面法线和光照方向的点积去调,光线颜色的强度。那么怎么计算顶点法线呢?下面的图介绍的很清楚了:
第一部分:
物体受到光照时要考虑的三种现象,
1.环境光(ambient)
现实中在有光情况下就会产生颜色,物体对每种颜色的吸收程度不同,所以产生不同的颜色,环境光公式:
Cambient =Iambient *Mambient(I为照射到物体的光照,M是物体对每种光的吸收率)
2.漫反射光(diffuse)
物体对光照产生的漫反射效果,实际情况中由于物体见面的不光滑,漫反射效果是不一样的,我们这里假设漫反射效果一样:
Cdiffuse = Idiffuse *Mdiffuse * cosθ(I为光照,M是漫反射率,θ是光源方向和顶线法向量夹角)当θ小于0时,没有漫反射
3.镜面反射(specular)
物体对光照产生的镜面反射效果
Cspecular =Ispecular *Mspecular * cosθ(I为光照,M是镜面反射率,θ是反射后光方向和观察点方向夹角) 当θ小于0时,没有镜面反射
第二部分:
光源类型:
1.平行光:模拟太阳光照效果,光只有方向,而且方向不变,没有衰减。
因为平行光没有衰减,计算平行光效果只需要将环境光,漫反射和镜面反射相加即可
Idir =Cambient+Cdiffuse+Cspecular
C++中的平行光定义
struct DirectionalLight{ DirectionalLight() { ZeroMemory(this, sizeof(this)); } XMFLOAT4 Ambient;//每种光都对应3中处理效果,1.环境光 XMFLOAT4 Diffuse;//2.漫反射光 XMFLOAT4 Specular;//3.镜面光 XMFLOAT3 Direction;//光照方向,平行光自己得属性 float Pad;// 用于与HLSL中“4D向量”对齐规则匹配,然而并不知道干什么用,个人理解是为了凑成4维向量方便计算};.fx文件中的平行光定义
struct DirectionalLight { float4 Ambient;//环境光 float4 Diffuse;//漫反射 float4 Specular;//镜面光 float3 Direction;//平行光方向 float pad; }
2.点光源:模拟灯光效果,有光源,有范围,有衰减
点光源有衰减,要计算衰减效果,超出范围就没有光照效果
设a是常量衰减因子,b是一次衰减因子,c是二次衰减因子,d是光源到物体距离
衰减因子计算方法 K = 1/(a+b*d+c*d*d) //d的2次方不会打
点光源效果计算方法:Ipoint = Idir*K//平行光乘以衰减系数
C++中点光源定义:
struct PointLight{ PointLight() { ZeroMemory(this, sizeof(this)); } XMFLOAT4 Ambient;//环境光 XMFLOAT4 Diffuse;//漫反射 XMFLOAT4 Specular;//镜面反射 // 点光源自己属性 XMFLOAT3 Position;//光源位置 float Range; //光照范围 XMFLOAT3 Att; // 衰减系数(参数a,b,c,d) float Pad;};
.fx文件中的点光源定义
struct PointLight { float4 Ambient; float4 Diffuse; float4 Specular; float3 Position;//光源位置 float Range;//范围 float3 Att;//衰减系数 float pad;};3.聚光灯:类似手电筒,光照体分为两个部分,一个内椎体一个外锥体。内椎体光照随距离增大而衰减,外锥体随角度和距离增大都衰减
聚光灯效果衰减算法:设内椎体角度a,外椎体角度b,角度衰减系数S,光线和聚光灯照射方向夹角θ
1.K = [(cosθ - cos(b/2))/(cos(a/2)-cos(b/2))]的S次方(不会打S次方)(S一般设为1) //这是在内锥形和外锥形之间算法
2.当角度小于内椎体时,计算方法和点光源一样
3.当角度大于外锥体时,不计算
C++中的聚光灯定义
struct SpotLight{ SpotLight() { ZeroMemory(this, sizeof(this)); } XMFLOAT4 Ambient; XMFLOAT4 Diffuse; XMFLOAT4 Specular; XMFLOAT3 Position;//光照位置 float Range;//光照范围 XMFLOAT3 Direction;//光照方向 float Spot;//光照角度衰减系数 XMFLOAT3 Att;//距离衰减系数 float Pad; };.fx中的聚光灯定义
<pre name="code" class="cpp"> struct SpotLight { float4 Ambient; float4 Diffuse; float4 Specular; float3 Position; float Range; float3 Direction; float Spot; float3 Att; float pad;};
第二部分:材质
C++中的定义
<pre name="code" class="cpp">struct Material{ Material() { ZeroMemory(this, sizeof(this)); } XMFLOAT4 Ambient;//环境光反射率 XMFLOAT4 Diffuse;//漫反射反射率 XMFLOAT4 Specular;//镜面反射反射率 XMFLOAT4 Reflect;//镜面光反射系数(随夹角影响的)};
以下是处理三种光源的具体代码
1.平行光
float4 ComputeDirectionalLight(Material mat, <span style="white-space:pre"></span>//材质 DirectionalLight L, <span style="white-space:pre"></span>//平行光 float3 normal, <span style="white-space:pre"></span>//顶点法线 float3 toEye, <span style="white-space:pre"></span>//顶点到眼睛的向量 out float4 ambient, <span style="white-space:pre"></span>//计算结果:环境光 out float4 diffuse, <span style="white-space:pre"></span>//计算结果:漫反射光 out float4 spec) <span style="white-space:pre"></span>//计算结果:镜面反射光{ // 结果初始化为0<span style="white-space:pre"></span>这三个是输出值 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); // 光线方向<span style="white-space:pre"></span>光线方向和光照方向相反 float3 lightVec = -L.Direction; // 环境光直接计算 ambient = mat.Ambient * L.Ambient; // 计算漫反射系数 //光线、法线方向归一化 //HLSL内置函数dot计算向量点积 float diffuseFactor = dot(lightVec, normal); // 顶点背向光源不再计算 if (diffuseFactor > 0.0f) { //计算漫反射光 diffuse = diffuseFactor * mat.Diffuse * L.Diffuse; //计算高光
<pre name="code" class="cpp"><span style="white-space:pre"></span>//reflect求反射向量的 反射光专用 float3 v = reflect(-lightVec, normal); float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);spec = specFactor * mat.Specular * L.Specular; }
return ambient+diffuse+spec;}2.点光源
float4 ComputePointLight(Material mat, <span style="white-space:pre"></span>//材质 PointLight L, <span style="white-space:pre"></span>//点光源 float3 pos, //顶点位置 float3 normal, <span style="white-space:pre"></span>//顶点法线 float3 toEye, <span style="white-space:pre"></span>//顶点到眼睛的向量 out float4 ambient, <span style="white-space:pre"></span>//计算结果:环境光 out float4 diffuse, <span style="white-space:pre"></span>//计算结果:漫反射光 out float4 spec) <span style="white-space:pre"></span>//计算结果:镜面反射{ 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); 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;
</pre><pre name="code" class="cpp"> return<span style="white-space:pre"></span>ambient+diffuse+spec;}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); if (diffuseFactor > 0.0f) { float3 v = reflect(-lightVec, normal);
//pow乘方函数 float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w); //漫反射光 diffuse = diffuseFactor * mat.Diffuse * L.Diffuse; //高光 spec = specFactor * mat.Specular * L.Specular; } //聚光衰减系数角度衰减系数spot 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;
return ambient+diffuse+spec;
}
4. .fx文件
cbuffer cbPerFrame{ DirectionalLight <span style="white-space:pre"></span>gDirLight;<span style="white-space:pre"></span>//平行光属性描述 PointLight <span style="white-space:pre"></span>gPointLight;<span style="white-space:pre"></span>//点光源属性描述 SpotLight <span style="white-space:pre"></span>gSpotLight;<span style="white-space:pre"></span>//聚光灯属性描述 float3 <span style="white-space:pre"></span>gEyePosW; <span style="white-space:pre"></span>//观察点坐标};cbuffer cbPerObject{ float4x4 gWorld; float4x4 gWorldInvTranspose;<span style="white-space:pre"></span>//世界矩阵的逆矩阵的转置 float4x4 gWorldViewProj; Material gMaterial;};struct VertexIn{ float3 PosL : POSITION; <span style="white-space:pre"></span>//顶点坐标 float3 NormalL : NORMAL; <span style="white-space:pre"></span>//顶点法线};struct VertexOut{ float4 PosH : SV_POSITION; <span style="white-space:pre"></span>//投影后的坐标 float3 PosW : POSITION; <span style="white-space:pre"></span>//世界变换后的坐标 float3 NormalW : NORMAL; <span style="white-space:pre"></span>//世界变换后的顶点法线};
//进行坐标变换处理VertexOut VS(VertexIn vin){ VertexOut vout;
//局部坐标变换为世界坐标,变换后的顶点法线 vout.PosW = mul(float4(vin.PosL, 1.0f), gWorld).xyz; vout.NormalW = mul(vin.NormalL, (float3x3)gWorldInvTranspose); //投影变换<span style="white-space:pre"></span>mul函数 向量相乘 然而这个函数使用起来貌似有问题 具体什么问题可以网上查下 vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj); return vout;}float4 PS(VertexOut pin) : SV_Target{
float4 finalColor; //插值运算有可能使法线不再单位化,重新单位化法线 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更新到最终结果中 finalColor = ComputeDirectionalLight(gMaterial, gDirLight, pin.NormalW, toEyeW, A, D, S); finalColor += ComputePointLight(gMaterial, gPointLight, pin.PosW, pin.NormalW, toEyeW, A, D, S); finalColor += ComputeSpotLight(gMaterial, gSpotLight, pin.PosW, pin.NormalW, toEyeW, A, D, S); //最终颜色透明度使用漫反射光的 finalColor.a = gMaterial.diffuse.a; return finalColor;}
//此函数可以绑定着色器接口并绘制,C++中绘制只需要调用此函数就好technique11 LightTech{ pass P0 { SetVertexShader(CompileShader(vs_5_0, VS())); SetGeometryShader(NULL); SetPixelShader(CompileShader(ps_5_0, PS())); }}
- 3D图形学编程基础-基于Direct3D11-学习记录(二)光照模型的实现
- 3D图形学编程基础-基于Direct3D11-学习记录(三)纹理的使用
- 3D图形学编程基础-基于Direct3D11-学习记录(四)混合的使用
- 3D图形学编程基础-基于Direct3D11-学习记录(一)初始化DX设备,实现立方体绘制
- 基于Direct3D11的光照模型展示
- 图形学(7)光照模型
- 计算机图形学笔记(二),光照模型(cg语言的范例)
- 三维游戏图形学学习笔记(一)基本光照模型
- 游戏引擎基础(二)(3D环境的光照和纹理)
- GLSL着色语言的学习(二)光照模型
- 图形学理论 光照模型
- 计算机图形学-光照模型
- 计算机图形学学习记录(二)
- 3D计算机图形学,数学基础学习笔记(常用的坐标系解析)
- 计算机图形学 学习笔记(十二):颜色模型,简单 / 增量 光照模型
- 基于GPU实现的经典光照模型算法:漫反射模型(使用cg语言实现)
- 3D局部光照模型
- 3D局部光照模型
- java Main 命令行
- BlockChain技术系列(五)- 拜占庭共识
- Java中的深拷贝和浅拷贝介绍
- binary (数位DP)
- 深入理解Java的接口和抽象类
- 3D图形学编程基础-基于Direct3D11-学习记录(二)光照模型的实现
- opencv的图像载入、显示和输出
- Java Map 集合类简介
- Javascript、Jquery获取浏览器和屏幕各种高度宽度
- 一个好用的套路
- visio2010激活方法详解
- 【二分图】目录
- 在页面完成后加载多个函数
- fatal error RC1107: invalid usage; use RC /? for Help