Direct3D:对定向光源的代码实现 + 讨论分析

2024-01-22 05:08

本文主要是介绍Direct3D:对定向光源的代码实现 + 讨论分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

整体流程图
【完整代码见文章结尾】


一、定向光源代码实现

1.1 顶点法线

  使用顶点法线,同一个面的多个顶点的法线就不一定相同,这样通过光栅化处理后,就能在多面体的表面获得一种平滑过渡的光照效果。

// 使用顶点法线
struct Vertex{Vertex(float x, float y, float z,float nx, float ny, float nz){_x  = x;  _y  = y;  _z  = z;_nx = nx; _ny = ny; _nz = nz;}float _x, _y, _z;float _nx, _ny, _nz;static const DWORD FVF;
};

1.2 灵活顶点格式

  灵活顶点格式(Flexible Vertex Format, FVF)用来描述在顶点缓冲区中的顶点存储格式中包含了哪些属性,可以使程序只使用它需要的顶点数据,排除那些它不需要的组成成分,达到节省内存空间的目的。

// 描述包含位置属性、法向量属性的顶点结构时,灵活顶点格式标志
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;

1.3 设置材质

  首先创建一个ID3DXMesh的全局变量指针,这是是一个三角形图元的网格接口。之后再创建一个D3DMATERIAL9来储存茶壶的材质属性。

ID3DXMesh*   Teapot = 0;
D3DMATERIAL9 TeapotMtrl;

  下面的代码是对茶壶材质的初始化。ZeroMemory开辟内存空间,用0来填充内存区域。D3DMATERIAL9结构体是物体表面对光的反射百分比(没有被吸收的)。物体的颜色=反射环境光+反射漫反射光+反射镜面反射光+自发光

  在本次实验中,目标是制作一个灰色陶瓷质感的茶壶,所以在设置反射颜色的时候的 r, g, b 的值相等。因为是陶瓷质感,可能反射光会多一点,所以镜面光的反射值比漫反射和环境光的值大。当然茶壶不会自发光,所以Emissive的值为0 。Power是高光的锐利值,该值越大表示高光强度与周围亮度相差越大。从图 1 和图 2 的对比中可以直观看出Power值对材质所产生的作用。

// 初始化茶壶材料
::ZeroMemory(&TeapotMtrl, sizeof(TeapotMtrl));
TeapotMtrl = d3d::WHITE_MTRL;	//一个白色茶杯
TeapotMtrl.Diffuse  = D3DXCOLOR(0.1f, 0.1f, 0.1f, 0.5f);	// 漫反射光
TeapotMtrl.Specular = D3DXCOLOR(2.0f, 2.0f, 2.0f, 1.0f);	// 镜面光
TeapotMtrl.Ambient  = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);	// 环境光
TeapotMtrl.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);	// 发光度
TeapotMtrl.Power    = 15.0f;		//高光的锐利值,该值越大表示高光强度与周围亮度相差越大。// 创建一个茶壶
D3DXCreateTeapot(Device, &Teapot, 0);

Power值对材质所产生的作用

1.4 设置定向光源

  创建一个名为dir的定向光,为其开辟内存空间。设置定向光的颜色,分别设置漫反射、镜面光、环境光的颜色。设置光源的照射方向,从屏幕外侧左上角向中心照射。

// 设置光线
// 设置光的颜色,D3DXCOLOR的四个参数分别是r,g,b,a
D3DLIGHT9 dir;
::ZeroMemory(&dir, sizeof(dir));
dir.Type      = D3DLIGHT_DIRECTIONAL; //定向光源
dir.Diffuse   = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);		// 光源发出的漫反射光的颜色
dir.Specular  = D3DXCOLOR(0.2f, 0.2f, 0.2f, 1.0f);		// 光源发出的镜面光颜色
dir.Ambient   = D3DXCOLOR(0.6f, 0.6f, 0.6f, 1.0f);		// 光源发出的环境光的颜色
dir.Direction = D3DXVECTOR3(9.8f, -6.0f, 0.8f);		// 用向量表示的光源世界坐标照射方向(屏幕外侧左上角,向中心照射)

  设置光源编号和打开光源。

Device->SetLight(0, &dir);			// 设置0号灯
Device->LightEnable(0, true);		//打开0号灯光

1.5 渲染

  使用SetRenderState设置渲染状态,设置重新计算法线方向,自动对法线矢量进行归一化处理。打开镜面光渲染,这样会增加计算时间,但是可以提高渲染效果,图 3 和图 4 中分别展示了打开和关闭镜面光的渲染效果。设置混合因子,使alpha组件决定透明度。

// SetRenderState的两个参数:要改变的状态,状态值
// 打开高光并重新正规化
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);	// 设置渲染状态重新计算法线方向
Device->SetRenderState(D3DRS_SPECULARENABLE, true);		// 打开镜面光渲染// 设置混合因子,使alpha组件决定透明度
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

镜面光的渲染效果

1.6 设置摄像机

  设置摄像机的观察方向、摄像机的位置、摄像机的向上的方向。图 5和图 6是对up参数的对比分析。

// 设置摄像机
D3DXVECTOR3 pos(0.0f, 0.0f, -3.0f);		//眼睛的位置,观察的方向
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);	//摄影机的前进和后退,上下左右
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);		//向上的方向
D3DXMATRIX V;
D3DXMatrixLookAtLH(&V, &pos, &target, &up);
Device->SetTransform(D3DTS_VIEW, &V);

up参数的对比分析

1.7 设置投影

  使用D3DXMatrixPerspectiveFovLH进行投影变换,其中分别设置了y轴视角、宽高比、近裁面和远裁面。

// 设置投影矩阵。
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(&proj,D3DX_PI * 0.26f, // 90 - degree,y轴视角(float)Width / (float)Height,	// 宽高比1.0f,		//近裁面1000.0f);	//远裁面
Device->SetTransform(D3DTS_PROJECTION, &proj);

y轴视角与远近裁面的分析


二、实验分析

2.1 三种光源对比

  使用d3dUtility.h中的光源初始化函数,分别简单实现了三种光源,进行效果的对比。光源的方向均为从屏幕内侧右上角向中心照射。光源的颜色均为蓝色。从图 11、图 12、图 13 的对比中可以看出三种光的区别,但是因为点光源和聚光灯光源都是近似的发散性光源,所以在图中的区别不大。

D3DXVECTOR3 position(9.8f, 6.0f, 0.8f);		 //屏幕内侧右上角
D3DXVECTOR3 direction(-9.8f, -6.0f, -0.8f);  //屏幕内侧右上角,向中心照射
D3DXCOLOR blue(0.0f, 0.0f, 1.0f, 1.0f);// 蓝色聚光灯
D3DLIGHT9 blue_spot = d3d::InitSpotLight(&position, &direction, &blue);
// 蓝色点光源
D3DLIGHT9 blue_point = d3d::InitPointLight(&position, &blue);
// 蓝色定向光源
D3DLIGHT9 blue_direct = d3d::InitDirectionalLight(&direction, &blue);

三种光源对比

2.2 同时打开两个定向光

  图 14 中展示的是同时打开两个定向光的情况,一个是最初在实验中设置的白色定向光,从屏幕外侧左上角向中心照射,另一个是上文中提到的蓝色定向光,从屏幕内侧右上角向中心照射。

Device->SetLight(0, &dir);			// 设置0号灯
Device->LightEnable(0, true);		//打开0号灯光
Device->SetLight(1, &blue_direct);	// 设置1号灯
Device->LightEnable(1, true);		//打开1号灯光

同时打开两个定向光

2.3 对茶壶材质的参数调整

  对漫反射参数的调整,从图中可以看出,漫反射值过大会产生局部过曝现象。另外diffuse光是方向性的,所以diffuse光的入射角度决定了整体反射的强度,当入射光线跟顶点法向量平行时,diffuse反射是最强的,即为图 16中过曝部分。
对漫反射参数的调整
  对镜面反射参数的调整,从图中可以看出,当镜面反射的参数过小时,材质会感觉变的粗糙,有一种磨砂的质感,当镜面反射的参数变大时,材质的质感会更加光滑。
对镜面反射参数的调整
  对环境光的参数进行调整,环境反射是非方向性的,当环境反射很小时会导致材质变暗。
对环境光的参数进行调整
  对茶壶漫反射透明度参数的调整,D3DXCOLOR结构体的最后一个参数是alpha值,区间在[0, 1]之间。由下图可以看出alpha值对物体材质的影响。
对茶壶漫反射透明度参数的调整

2.4 对定向光的参数调整

  为了使效果更加明显,只打开0号光源,将另外一个蓝色的定向光关闭。

  对漫反射光的参数调整,从图中可以看出光源强度对物体局部亮度产生的影响。
对漫反射光的参数调整
  对镜面光的参数进行调整,可以看出光源镜面光过强时会对物体产生剧烈的过曝现象。但是这样的变化只产生在物体的局部表面,即朝向光的方向的表面,而背面则不会产生任何影响,由此可以证明镜面光是有很强方向性的。
对镜面光的参数进行调整
  对环境光的参数进行改变,从图中的对比可以看出环境光是没有方向性的,它与上面两种光不同,它的值改变会对整个环境中物体产生亮度上的影响,但是上面两种光只会对照射方向产生影响。
对环境光的参数进行改变
  对定向光的方向进行参数改变,改变其z值,从图中可以看出光线的方向产生了变化。
对定向光的方向进行参数改变


三、实验总结

  在本实验中主要实现了定向光的案例,其中有两个比较关键的地方:设置茶壶材质、设置光源参数。

  材质是物体的一个基本属性,它反映了物体表面对光的反射百分比,其中主要有5个参数Diffuse(漫反射),Ambient(环境光反射),Specular(镜面反射),Emissive(发光),Power(高光的锐利值),通过对这些参数的调整,可以模拟出各种材质。

  光源的参数设置主要储存在D3DLIGHT9结构体中。和材质相同的是它也包括上述的三种光,除此之外,它还有光源的位置、光源的方向、光的衰减度等参数。在本身实验中主要使用的是定向光,定向光是类似太阳光的平行光,没有发出的光源位置,只有光线的方向。


四、附录

DirectionaLight.cpp完整代码

//
// 
// File: DirectionaLight.cpp
// 
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 
//
// Desc: Renders a semi transparent teapot using alpha blending.  In this 
//       sample, the alpha is taken from the material's diffuse alpha value.
//       You can increase the opaqueness with the 'Q' key and can descrease
//       it with the 'E' key.          
//#include "d3dUtility.h"// Globals
IDirect3DDevice9* Device = 0; 
const int Width  = 640;
const int Height = 480;ID3DXMesh*   Teapot = 0;
D3DMATERIAL9 TeapotMtrl;// 使用顶点法线
struct Vertex{Vertex(float x, float y, float z,float nx, float ny, float nz){_x  = x;  _y  = y;  _z  = z;_nx = nx; _ny = ny; _nz = nz;}float _x, _y, _z;float _nx, _ny, _nz;static const DWORD FVF;
};
// 描述包含位置属性、法向量属性的顶点结构时,灵活顶点格式标志
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;// 用来完成例子所需的所有设置,例如完成资源分配、设备性能检测以及程序状态设置等工作
bool Setup(){// 初始化茶壶材料::ZeroMemory(&TeapotMtrl, sizeof(TeapotMtrl));TeapotMtrl = d3d::WHITE_MTRL;	//一个白色茶杯TeapotMtrl.Diffuse  = D3DXCOLOR(0.1f, 0.1f, 0.1f, 0.5f);	// 漫反射光TeapotMtrl.Specular = D3DXCOLOR(2.0f, 2.0f, 2.0f, 1.0f);	// 镜面光TeapotMtrl.Ambient  = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);	// 环境光TeapotMtrl.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);	// 发光度TeapotMtrl.Power    = 15.0f;		//高光的锐利值,该值越大表示高光强度与周围亮度相差越大。// 创建一个茶壶D3DXCreateTeapot(Device, &Teapot, 0);// 设置光线// 设置光的颜色,D3DXCOLOR的四个参数分别是r,g,b,aD3DLIGHT9 dir;::ZeroMemory(&dir, sizeof(dir));dir.Type      = D3DLIGHT_DIRECTIONAL; //定向光源dir.Diffuse   = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);		// 光源发出的漫反射光的颜色dir.Specular  = D3DXCOLOR(0.2f, 0.2f, 0.2f, 1.0f);		// 光源发出的镜面光颜色dir.Ambient   = D3DXCOLOR(0.6f, 0.6f, 0.6f, 1.0f);		// 光源发出的环境光的颜色dir.Direction = D3DXVECTOR3(9.8f, -6.0f, 0.8f);		// 用向量表示的光源世界坐标照射方向(屏幕外侧左上角,向中心照射)D3DXVECTOR3 position(9.8f, 6.0f, 0.8f);		 //屏幕内侧右上角D3DXVECTOR3 direction(-9.8f, -6.0f, -0.8f);  //屏幕内侧右上角,向中心照射D3DXCOLOR blue(0.0f, 0.0f, 1.0f, 1.0f);// 蓝色聚光灯D3DLIGHT9 blue_spot = d3d::InitSpotLight(&position, &direction, &blue);// 蓝色点光源D3DLIGHT9 blue_point = d3d::InitPointLight(&position, &blue);// 蓝色定向光源D3DLIGHT9 blue_direct = d3d::InitDirectionalLight(&direction, &blue);Device->SetLight(0, &dir);			// 设置0号灯Device->LightEnable(0, true);		//打开0号灯光Device->SetLight(1, &blue_direct);	// 设置1号灯Device->LightEnable(1, true);		//打开1号灯光// SetRenderState的两个参数:要改变的状态,状态值// 打开高光并重新正规化Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);	// 设置渲染状态重新计算法线方向Device->SetRenderState(D3DRS_SPECULARENABLE, true);		// 打开镜面光渲染// 设置混合因子,使alpha组件决定透明度Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);// 设置摄像机D3DXVECTOR3 pos(0.0f, 0.0f, -3.0f);		//眼睛的位置,观察的方向D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);	//摄影机的前进和后退,上下左右D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);		//向上的方向D3DXMATRIX V;D3DXMatrixLookAtLH(&V, &pos, &target, &up);Device->SetTransform(D3DTS_VIEW, &V);// 设置投影矩阵。D3DXMATRIX proj;D3DXMatrixPerspectiveFovLH(&proj,D3DX_PI * 0.26f, // 90 - degree,y轴视角(float)Width / (float)Height,	// 宽高比1.0f,		//近裁面1000.0f);	//远裁面Device->SetTransform(D3DTS_PROJECTION, &proj);return true;
}// 用于释放Setup函数中所分配的资源,例如释放在堆上占用的内存空间
void Cleanup(){	d3d::Release<ID3DXMesh*>(Teapot);
}// 该函数包含了所有的渲染代码以及那些在每帧之间控制程序的操作代码
bool Display(float timeDelta){if( Device ){// 通过键盘输入增加/减少alpha值if( ::GetAsyncKeyState('Q') & 0x8000f )TeapotMtrl.Diffuse.a += 0.001f;if( ::GetAsyncKeyState('E') & 0x8000f )TeapotMtrl.Diffuse.a -= 0.001f;// 透明度[0,1]区间if(TeapotMtrl.Diffuse.a > 1.0f)TeapotMtrl.Diffuse.a = 1.0f;if(TeapotMtrl.Diffuse.a < 0.0f)TeapotMtrl.Diffuse.a = 0.0f;// Render 渲染Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);Device->BeginScene();Device->SetMaterial(&TeapotMtrl);//设置设备的材料属性Device->SetTexture(0, 0);	//将纹理分配给设备的舞台Teapot->DrawSubset(0);		//绘制网格的子集Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);	// 设置支持透明度的渲染Device->EndScene();Device->Present(0, 0, 0, 0);}return true;
}// WndProc
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{switch( msg ){case WM_DESTROY:::PostQuitMessage(0);break;		case WM_KEYDOWN:if( wParam == VK_ESCAPE )::DestroyWindow(hwnd);break;}return ::DefWindowProc(hwnd, msg, wParam, lParam);
}// WinMain
int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE prevInstance, PSTR cmdLine,int showCmd){if(!d3d::InitD3D(hinstance,Width, Height, true, D3DDEVTYPE_HAL, &Device)){::MessageBox(0, "InitD3D() - FAILED", 0, 0);return 0;}if(!Setup()){::MessageBox(0, "Setup() - FAILED", 0, 0);return 0;}d3d::EnterMsgLoop( Display );Cleanup();Device->Release();return 0;
}

【如需要其它配置文件代码,可私信博主】

这篇关于Direct3D:对定向光源的代码实现 + 讨论分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/631919

相关文章

Python实现终端清屏的几种方式详解

《Python实现终端清屏的几种方式详解》在使用Python进行终端交互式编程时,我们经常需要清空当前终端屏幕的内容,本文为大家整理了几种常见的实现方法,有需要的小伙伴可以参考下... 目录方法一:使用 `os` 模块调用系统命令方法二:使用 `subprocess` 模块执行命令方法三:打印多个换行符模拟

SpringBoot+EasyPOI轻松实现Excel和Word导出PDF

《SpringBoot+EasyPOI轻松实现Excel和Word导出PDF》在企业级开发中,将Excel和Word文档导出为PDF是常见需求,本文将结合​​EasyPOI和​​Aspose系列工具实... 目录一、环境准备与依赖配置1.1 方案选型1.2 依赖配置(商业库方案)二、Excel 导出 PDF

Python实现MQTT通信的示例代码

《Python实现MQTT通信的示例代码》本文主要介绍了Python实现MQTT通信的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 安装paho-mqtt库‌2. 搭建MQTT代理服务器(Broker)‌‌3. pytho

使用zip4j实现Java中的ZIP文件加密压缩的操作方法

《使用zip4j实现Java中的ZIP文件加密压缩的操作方法》本文介绍如何通过Maven集成zip4j1.3.2库创建带密码保护的ZIP文件,涵盖依赖配置、代码示例及加密原理,确保数据安全性,感兴趣的... 目录1. zip4j库介绍和版本1.1 zip4j库概述1.2 zip4j的版本演变1.3 zip4

MySQL进行数据库审计的详细步骤和示例代码

《MySQL进行数据库审计的详细步骤和示例代码》数据库审计通过触发器、内置功能及第三方工具记录和监控数据库活动,确保安全、完整与合规,Java代码实现自动化日志记录,整合分析系统提升监控效率,本文给大... 目录一、数据库审计的基本概念二、使用触发器进行数据库审计1. 创建审计表2. 创建触发器三、Java

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

python生成随机唯一id的几种实现方法

《python生成随机唯一id的几种实现方法》在Python中生成随机唯一ID有多种方法,根据不同的需求场景可以选择最适合的方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习... 目录方法 1:使用 UUID 模块(推荐)方法 2:使用 Secrets 模块(安全敏感场景)方法

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建