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 pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

JAVA利用顺序表实现“杨辉三角”的思路及代码示例

《JAVA利用顺序表实现“杨辉三角”的思路及代码示例》杨辉三角形是中国古代数学的杰出研究成果之一,是我国北宋数学家贾宪于1050年首先发现并使用的,:本文主要介绍JAVA利用顺序表实现杨辉三角的思... 目录一:“杨辉三角”题目链接二:题解代码:三:题解思路:总结一:“杨辉三角”题目链接题目链接:点击这里

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

SpringBoot使用注解集成Redis缓存的示例代码

《SpringBoot使用注解集成Redis缓存的示例代码》:本文主要介绍在SpringBoot中使用注解集成Redis缓存的步骤,包括添加依赖、创建相关配置类、需要缓存数据的类(Tes... 目录一、创建 Caching 配置类二、创建需要缓存数据的类三、测试方法Spring Boot 熟悉后,集成一个外

SpringBoot实现基于URL和IP的访问频率限制

《SpringBoot实现基于URL和IP的访问频率限制》在现代Web应用中,接口被恶意刷新或暴力请求是一种常见的攻击手段,为了保护系统资源,需要对接口的访问频率进行限制,下面我们就来看看如何使用... 目录1. 引言2. 项目依赖3. 配置 Redis4. 创建拦截器5. 注册拦截器6. 创建控制器8.

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用