DirectX学习笔记(五):利用平面着色和Gouraud着色模式绘制具有颜色的三角形

前言:

图元的颜色是由构成该图元的顶点的颜色所决定的,而物体是由图元索组成。那么如果我们要绘制一个具有颜色的三角形,我们就需要为三角形图元顶点附加颜色属性。

1.Direct3D中颜色的表示:

在Direct3D中,颜色用RGB来表示(这个大家都知道,咳咳。)通畅人为颜色可以分为红色(red)、绿色(green)、蓝色(bule)。这三个分量进行混合,以达到我们想要的颜色取得目的。RGB的数据可以用两种方式来进行存储一种是D3DCOLOR ,另一种是:DWORD。这是因为D3DCOLOR完全等同于DWORD(在MSDN中:

typedef DWORD D3DCOLOR;)关于Alpha分量,先不进行说明。因为这个现在用不到。

要指定每个颜色分量的值,并将其插入到D3DCOLOR中,需要使用D3DCOLOR_ARPG宏。这个宏帮我们实现了二进制的运算。

使用方式:

D3DCOLOR brightRed = D3DCOLOR_ARGB(255, 255, 0, 0);

另外介绍另外一种颜色数据的存储结构:D3DCOLORVALUE。

typedef struct _D3DCOLORVALUE {
    float r;
    float g;
    float b;
    float a;
} D3DCOLORVALUE;

用单精度数来度量每个分量的亮度值,取值范围为0~1.

但是这种结构很少用到。因为D3DCOLORVALUE结构可以转换为D3DCOLOR,而且D3DCOLOR比D3DCOLORVALUE所包含的功能方法更为强大。

 2.着色:

这里先说一下什么是光栅化:简单理解,我们在现实生活中,如果绘制一个三角形,每条线是由无数个无穷小的点构成的。但是在计算机中,没有这个无穷小的处理方法,在屏幕上的三角形是由很多点像素构成,将连续的图像用离散的像素表示的过程就是光栅化。

在光栅化的过程中,需要对三角形进行着色。着色规定了如何利用顶点颜色来计算构成像素的颜色。在Direct3D中有两种着色模式:

第一种:平面着色:每个图元的像素都被一致的赋予该图元的第一个顶点的颜色。

第二种:Gouraud着色:土元种的各像素的颜色值由顶点的颜色经线性插值得到。

那么接下来我们就用这两种着色模式来绘制两个三角形:

要完成的操作:

1.定义顶点结构:

struct Vertex //顶点结构
{

	Vertex(){}
	Vertex(float x, float y, float z, D3DCOLOR  color)
	{
		_x = x;  _y = y;  _z = z;
		_color = color;
	}
	float _x, _y, _z;
	D3DCOLOR _color;
	static const DWORD FVF;
};
//顶点格式:说明对应于顶点格式的顶点结构包含了位置属性和漫反射颜色属性
//要注意的是,灵活顶点格式标记的指定顺序必须与顶点结构中相应类型数据的顺序保持一致。
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;

2.创建顶点缓存,将顶点颜色等数据写入:

//创建顶点缓存
	Device->CreateVertexBuffer(3 * sizeof(Vertex), D3DUSAGE_WRITEONLY, Vertex::FVF, D3DPOOL_MANAGED, &Triangle, 0);

	//访问顶点缓存,将Cube顶点数据写入
	Vertex* vertices;
	Triangle->Lock(0, 0, (void**)&vertices, 0);
	vertices[0] = Vertex(-1.0f, 0.0f, 2.0f, D3DCOLOR_XRGB(255, 0, 0));
	vertices[1] = Vertex(0.0f, 1.0f, 2.0f, D3DCOLOR_XRGB(0, 255, 0));
	vertices[2] = Vertex(1.0f, 0.0f, 2.0f, D3DCOLOR_XRGB(0, 0, 255));

	Triangle->Unlock();

3.投影变换:

在当摄像机的位置和朝向任意时,进行一些操作会造成效率低下很是麻烦。为了简化运算,我们将摄像机变换到世界坐标系的原点,并旋转摄像机使其光轴与世界坐标系的z轴朝向一致。要特别注意的是,世界中的所有物体都会随之摄像机变换,以保证摄像机的视场恒定。

我们先使用D3DXMatrixLookAtLH(...)函数来获取一个取景变换矩阵,然后利用::SetTransform(..)方法设定:

	//投影变换
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.5f, (float)Width / (float)Height, 1.0f, 1000.0f);
	Device->SetTransform(D3DTS_PROJECTION, &proj);

4. 设置绘制状态:

Device->SetRenderState(D3DRS_LIGHTING, true);  //默认状态下光照是启用的,这里将其禁用,因为我们没有使用到。

但是显示指定并无大碍

//设置绘制状态
	Device->SetRenderState(D3DRS_LIGHTING, false);

5.显示:

在绘制中,我们要绘制两个三角形,那么设置三角形的位置信息由世界变换矩阵World来控制:

//绘制
	Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);//屏幕清除,设为黑色背景
	Device->BeginScene();

	Device->SetFVF(Vertex::FVF); //设置灵活顶点格式

	//利用SetStreamSource设置顶点输入流的来源,将顶点缓存与数据流进行链接
	//实质是将几何体的信息传到绘制流水线中
	Device->SetStreamSource(0, Triangle, 0, sizeof(Vertex));

	// 利用平面着色模式绘制左边的三角形
	//设置位置
	D3DXMatrixTranslation(&WorldMatrix, -1.25f, 0.0f, 0.0f);
	Device->SetTransform(D3DTS_WORLD, &WorldMatrix);
	//绘制
	Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
	Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

	// 利用Grouaud模式绘制右边的三角形
	D3DXMatrixTranslation(&WorldMatrix, 1.25f, 0.0f, 0.0f);
	Device->SetTransform(D3DTS_WORLD, &WorldMatrix);
	//绘制
	Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
	Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

	Device->EndScene();
	Device->Present(0, 0, 0, 0);

完整代码:(可运行)

#include<d3d9.h>
#include<d3dx9math.h>
#include<windows.h>  

IDirect3DDevice9* Device = 0; // 一个C++对象,代表了我们用来显示3D图形的物理硬件设备
IDirect3DVertexBuffer9* Triangle = 0;//顶点缓存  

D3DXMATRIX WorldMatrix;
const int Width = 640; //窗口的宽度
const int Height = 480; //高度  

//-----------------------------顶点结构体----------------------------------
struct Vertex //顶点结构
{

	Vertex(){}
	Vertex(float x, float y, float z, D3DCOLOR  color)
	{
		_x = x;  _y = y;  _z = z;
		_color = color;
	}
	float _x, _y, _z;
	D3DCOLOR _color;
	static const DWORD FVF;
};
//顶点格式:说明对应于顶点格式的顶点结构包含了位置属性和漫反射颜色属性
//要注意的是,灵活顶点格式标记的指定顺序必须与顶点结构中相应类型数据的顺序保持一致。
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;

//------------------------------以下为窗口过程---------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)//窗口过程
{
	switch (msg)
	{
	case WM_DESTROY://销毁
		PostQuitMessage(0); //终止请求
		break;
	}
	//调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。
	//该函数确保每一个消息得到处理
	return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

//----------------------------以下为初始化窗口信息---------------------------------
bool InitWindow(HINSTANCE hInstance, HWND &hwnd, int width, int height)
{
	//定义窗口样式
	WNDCLASS wc;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(0, IDI_APPLICATION);
	wc.hCursor = LoadCursor(0, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = 0;
	wc.lpszClassName = "LszDX";

	//窗口注册
	RegisterClass(&wc);

	//创建窗口
	hwnd = ::CreateWindow("LszDX", "LszDX", WS_OVERLAPPEDWINDOW, 0, 0, width, height, 0, 0, hInstance, 0);

	//绘制更新窗口
	ShowWindow(hwnd, SW_SHOW);
	UpdateWindow(hwnd);
	return true;
}

//----------------------------以下为初始化Direct3D----------------------------------------
//注意函数的hwnd为传引用
bool InitD3D(HINSTANCE &hInstance, HWND &hwnd, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9** device)
{
	//获取IDirect3D9的指针
	IDirect3D9* d3d9 = 0;
	d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

	//检验硬件顶点运算
	D3DCAPS9 caps;
	d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps);

	int vp = 0;
	if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

	//填充D3DPRESENT_PARAMETERS 结构
	D3DPRESENT_PARAMETERS d3dpp;
	d3dpp.BackBufferWidth = width;
	d3dpp.BackBufferHeight = height;
	d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
	d3dpp.BackBufferCount = 1;
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
	d3dpp.MultiSampleQuality = 0;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hwnd;
	d3dpp.Windowed = windowed;
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	d3dpp.Flags = 0;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	//创建IDirect3DDevice9接口
	d3d9->CreateDevice(D3DADAPTER_DEFAULT, deviceType, hwnd, vp, &d3dpp, device);
	d3d9->Release();
	return true;
}

//投影变换
//设置绘制状态
bool InitTr()
{
	//创建顶点缓存
	Device->CreateVertexBuffer(3 * sizeof(Vertex), D3DUSAGE_WRITEONLY, Vertex::FVF, D3DPOOL_MANAGED, &Triangle, 0);

	//访问顶点缓存,将Cube顶点数据写入
	Vertex* vertices;
	Triangle->Lock(0, 0, (void**)&vertices, 0);
	vertices[0] = Vertex(-1.0f, 0.0f, 2.0f, D3DCOLOR_XRGB(255, 0, 0));
	vertices[1] = Vertex(0.0f, 1.0f, 2.0f, D3DCOLOR_XRGB(0, 255, 0));
	vertices[2] = Vertex(1.0f, 0.0f, 2.0f, D3DCOLOR_XRGB(0, 0, 255));

	Triangle->Unlock();

	//投影变换
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.5f, (float)Width / (float)Height, 1.0f, 1000.0f);
	Device->SetTransform(D3DTS_PROJECTION, &proj);

	//设置绘制状态
	Device->SetRenderState(D3DRS_LIGHTING, false);
	return true;
}

//-----------------------以下为对Cube进行绘制-------------------------------------
bool Display()
{
	MSG msg;
	ZeroMemory(&msg, sizeof(MSG));//用0来填充消息可类比为:memset()函数
	while (msg.message != WM_QUIT) //退出
	{
		//PeekMessage函数是以查看的方式从系统中获取消息
		//并将该消息(如果存在)放于指定的结构
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			//PM_REMOVE:PeekMessage处理后,消息从队列里除掉。  

			//TranslateMessage函数将虚拟键消息转换为字符消息。
			//字符消息被寄送到调用线程的消息队列里,
			//当下一次线程调用函数GetMessage或PeekMessage时被读出。
			//TranslateMessage只能用于转换调用GetMessage或PeekMessage接收的消息。    

			TranslateMessage(&msg);

			//DispatchMessage函数
			//该函数分发一个消息给窗口程序。
			//通常消息从GetMessage函数获得。
			//消息被分发到回调函数(过程函数),作用是消息传递给操作系统,
			//然后操作系统去调用我们的回调函数,也就是说我们在窗体的过程函数中处理消息
			DispatchMessage(&msg);
		}
		else
		{
			if (Device)
			{

				//绘制
				Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);//屏幕清除,设为白色背景
				Device->BeginScene();

				Device->SetFVF(Vertex::FVF); //设置灵活顶点格式

				//利用SetStreamSource设置顶点输入流的来源,将顶点缓存与数据流进行链接
				//实质是将几何体的信息传到绘制流水线中
				Device->SetStreamSource(0, Triangle, 0, sizeof(Vertex));

				// 利用平面着色模式绘制左边的三角形
				//设置位置
				D3DXMatrixTranslation(&WorldMatrix, -1.25f, 0.0f, 0.0f);
				Device->SetTransform(D3DTS_WORLD, &WorldMatrix);
				//绘制
				Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
				Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

				// 利用Grouaud模式绘制右边的三角形
				D3DXMatrixTranslation(&WorldMatrix, 1.25f, 0.0f, 0.0f);
				Device->SetTransform(D3DTS_WORLD, &WorldMatrix);

				//绘制
				Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
				Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

				Device->EndScene();
				Device->Present(0, 0, 0, 0);
			}
		}
	}
	return true;
}

//-------------------------Main函数-----------------------------------------------
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
{
	HWND hwnd = 0;

	//1.窗口创建
	InitWindow(hinstance, hwnd, Width, Height);

	//2.初始化Direct3D
	InitD3D(hinstance, hwnd, Width, Height, true, D3DDEVTYPE_HAL, &Device);

	//3.初始化cube信息
	InitTr();

	//4.进行显示绘制
	Display();

	//5.释放指针
	Device->Release();
	return 0;
}

运行效果:

时间: 2024-10-12 18:16:15

DirectX学习笔记(五):利用平面着色和Gouraud着色模式绘制具有颜色的三角形的相关文章

DirectX学习笔记(四):利用D3DX网格数据结构绘制可旋转茶壶

前言: 在上篇文章(DirectX学习笔记三)中,我详细的说明了如何利用线框模式绘制可旋转的正方体.链接:点击打开链接.但是应该看到的是,如果我们通过创建三角形单元来创建3D物体是十分繁琐的事情.幸运的是,在D3DX库中提供了一些用于生成简单3D物体的网格数据方法. 如:利用网格数据创建一个茶壶: 1. 我们需要使用ID3DXMesh网格数据结构接口来创建我们的茶壶网格数据,这时我们需要使用此函数: HRESULT D3DXCreateTeapot(LPDIRECT3DDEVICE9 pDevi

NLTK学习笔记(五):分类和标注词汇

[TOC] 词性标注器 之后的很多工作都需要标注完的词汇.nltk自带英文标注器pos_tag import nltk text = nltk.word_tokenize("And now for something compleyely difference") print(text) print(nltk.pos_tag(text)) 标注语料库 表示已经标注的标识符:nltk.tag.str2tuple('word/类型') text = "The/AT grand/J

学习笔记:利用GDI+生成简单的验证码图片

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 学习笔记:利用GDI+生成简单的验证码图片 1 /// <summary> 2 /// 单击图片时切换图片 3 /// </summary> 4 /// <param name="sender">&

jQuery源码学习笔记五 六 七 八 转

jQuery源码学习笔记五 六 七 八 转 Js代码   <p>在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等.接着是其缓存机制data.</p> <pre class="brush:javascript;gutter:false;toolbar:false"> //@author  司徒正美|なさみ|cheng http://www.cnblogs.com/ru

Lua学习笔记(五):函数

函数有两种用途: 1).完成指定的任务,这种情况下函数作为调用语句使用: 2).计算并返回值,这种情况下函数作为赋值语句表达式使用. 1 function func_name (arguments-list) 2 statements-list; 3 end; 4 5 --调用函数的时候,如果参数列表为空,必须使用()表明是函数调用 6 print(8*9, 9/8) 7 a = math.sin(3) + math.cos(10) 8 print(os.date()) 9 10 --上述规则有

laravel3学习笔记(五)

原作者博客:ieqi.net ==================================================================================================== 模型 在MVC模式的Web框架中,模型Model作为数据的抽象而存在,在Model层中,我们放置直接对数据的各种处理和操作,然后将抽象过的数据操作暴露为Model类给控制器,这样,在控制器中我们就不必拘泥于处理具体数据现实的各种细节中了,比如数据库如何连接,数据的类型

建筑建模学习笔记1——AutoCAD平面建模

建筑建模学习笔记1--AutoCAD平面建模 楼宇自控项目上位机控制展示软件需要展示成3D效果图,最近在学习3D建模的相关知识.3D建筑物建模多数都是导入2D的CAD建筑图纸,在其基础上进行建模,这样作出的3D模型则会完全与实物相符.现在记录一些AutoCAD画图的命令. 1.划线命令 1)Line 快捷键是 L,工具栏图标 下面是划线的操作动画: 划线时我们看到线不仅有长度还有角度,在在多数情况下画直角的线多一些,AutoCAD提供了正交按钮,在选中此项后,我们画的线与线间的夹角永远都是90度

Caliburn.Micro学习笔记(五)----协同IResult

Caliburn.Micro学习笔记(五)----协同IResult 今天说一下协同IResult 看一下IResult接口 /// <summary> /// Allows custom code to execute after the return of a action. /// </summary> public interface IResult { /// <summary> /// Executes the result using the specif

angular学习笔记(五)-阶乘计算实例(1)

<!DOCTYPE html> <html ng-app> <head> <title>2.3.2计算阶乘实例1</title> <meta charset="utf-8"> <script src="../angular.js"></script> <script src="script.js"></script> </