博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DirectX11 光照实现
阅读量:4087 次
发布时间:2019-05-25

本文共 5318 字,大约阅读时间需要 17 分钟。

光照实现

1. 光照结构体

在LightHelper.h文件中,我们定义了一些的结构体来表示平行光、点光或聚光灯。

结构体成员组成如下:

1.Ambient:由光源发射的环境光的数量。

2.Diffuse:由光源发射的漫反射光的数量。

3.Specular:由光源发射的高光的数量。

4.Direction:灯光方向。

5.Position:灯光位置。

6.Range:光照范围(离开光源的距离大于这个值的点不会被照亮)。

7.Attenuation:按照(a0、a1和a2)的顺序存储3个衰减常量。衰减常量只用于点和聚光灯,用于控制光强随距离衰减的程度。

8.Spot:该指数用于控制聚光灯的圆锥体区域大小;这个值只用于聚光灯。

“pad”变量的必要性和“packing”格式已经在上一篇博文中讨论过了,如果不知道什么意思,请务必回顾上一篇博文。

下面是各种光源的结构体定义:

struct DirectionalLight{    DirectionalLight() { ZeroMemory(this, sizeof(this)); }    XMFLOAT4 Ambient;    XMFLOAT4 Diffuse;    XMFLOAT4 Specular;    XMFLOAT3 Direction;    float Pad; // 占位最后一个float,这样我们就可以设置光源数组了。};struct PointLight{    PointLight() { ZeroMemory(this, sizeof(this)); }    XMFLOAT4 Ambient;    XMFLOAT4 Diffuse;    XMFLOAT4 Specular;    // 打包到4D矢量: (Position, Range)    XMFLOAT3 Position;    float Range;    // 打包到4D矢量: (A0, A1, A2, Pad)    XMFLOAT3 Att;    float Pad; // 占位最后一个float,,这样我们就可以设置光源数组了。};struct SpotLight{    SpotLight() { ZeroMemory(this, sizeof(this)); }    XMFLOAT4 Ambient;    XMFLOAT4 Diffuse;    XMFLOAT4 Specular;    // 打包到4D矢量: (Position, Range)    XMFLOAT3 Position;    float Range;    // 打包到4D矢量: (Direction, Spot)    XMFLOAT3 Direction;    float Spot;    // 打包到4D矢量: (Att, Pad)    XMFLOAT3 Att;    float Pad; // 占位最后一个float,,这样我们就可以设置光源数组了。};

定义在LightHelper.fx文件中的结构体镜像了上面的结构体:

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;};

2. 实现平行光

下面的HLSL函数根据给出的材质、平行光源、表面法线和由表面指向观察点的矢量,输出光照后的表面颜色。

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;     // 添加漫反射和镜面光    float diffuseFactor = dot(lightVec, normal);    // Flatten避免动态分支    [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;    }}

其中,使用的HLSL内置函数有:dot、reflect、pow和max,它们分别用来计算向量点积、向量反射、乘方和取最大值。读者可以在附录B中找到大部分HLSL内置函数的描述,以及有关HLSL语法的快速入门。另外还有一件事情需要注意,那就是当两个向量使用*运算符相乘时,实际执行的是分量乘法。

注意:PC上的HLSL函数总是内联的,调用函数或传递参数不会有性能损失。

3. 实现点光

下面的HLSL函数根据给出的材质、点光源、表面位置、表面法线和表面点到观察点的的矢量信息,输出光照后的表面颜色。

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避免动态分支    [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;}

4. 实现聚光灯

下面的HLSL函数根据给出的材质、聚光灯、表面位置、表面法线、表面点指向观察点位置的矢量信息,输出光照后的表面颜色:

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);         // Range test.    if( d > L.Range )        return;             // 规范化光照矢量    lightVec /= d;         // 计算环境光    ambient = mat.Ambient * L.Ambient;      // 计算漫反射和镜面光,所提供的表面是在该光线的位置射出的直线。     float diffuseFactor = dot(lightVec, normal);     // Flatten避免动态分支    [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;}

转载地址:http://zpyii.baihongyu.com/

你可能感兴趣的文章
我觉得嵌入式面试三要素:基础吃透+项目+大量刷题,缺一不可。不刷题是不行的。而且得是大量刷,刷出感觉套路,别人做题都做得是固定题型套路条件反射了,你还在那慢慢理解慢慢推是不行的,也是考研的教训。
查看>>
相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。
查看>>
现在来看,做个普罗米修斯的docker镜像对我而言并不难,对PX4仿真环境配置也熟悉了。
查看>>
删除docker容器和镜像的命令
查看>>
VINS-Fusion Intel® RealSense™ Depth Camera D435i
查看>>
使用Realsense D435i运行VINS-Fusion并建图
查看>>
gazebo似乎就是在装ROS的时候一起装了,装ROS的时候选择的是ros-melodic-desktop-full的话。
查看>>
React + TypeScript 实现泛型组件
查看>>
TypeScript 完全手册
查看>>
React Native之原理浅析
查看>>
Git操作清单
查看>>
基础算法
查看>>
前端面试
查看>>
React Hooks 异步操作踩坑记
查看>>
聊聊编码那些事,顺带实现base64
查看>>
TypeScript for React (Native) 进阶
查看>>
React 和 ReactNative 的渲染机制/ ReactNative 与原生之间的通信 / 如何自定义封装原生组件/RN中的多线程
查看>>
JavaScript实现DOM树的深度优先遍历和广度优先遍历
查看>>
webpack4 中的 React 全家桶配置指南,实战!
查看>>
react 设置代理(proxy) 实现跨域请求
查看>>