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

相关文章

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(