DirectSound之3D狼嚎

DirectSound之3D狼嚎

經多日努力終於可以在3D空間中添加中添加3D效果,所帶來極強真實感效果有時比3D渲染更好.而且x86和x64均正確運行.在演示程式中.『狼嚎』放置於山地中心.隨著按方向鍵移動相機聲音產生變化.單這點很多書本例程均不正確. 3D狼嚎程式:下載

而DirectSound如何模擬3D效果.3D即有xyz三軸笛卡爾座標,以此定義聲源和聽者位置、速度、方向.要注意Z軸是指向屏幕內部.所以3D效果需單聲道.

typdef struct{

IDirectSoundBuffer       * buffer;  // 輔助緩存

IDirectSound3DBuffer8    * buffer3D;// 3D緩存

float x,y,z;// 音源3D位置

PBYTE data;// 音頻數據

int size;// 所占空間

}SOUND3D;

SOUND3D sound3D;

而一般我地有左右兩個音箱,最多四角四個音箱.要模擬3D感知效果要素如下:

  1. 響度:當聲源逐漸遠離聽者,聲源會逐漸衰減
  2. 兩耳聲響差:若聲源位於聽者右側,則右耳聽到聲響比左耳響.
  3. 兩耳時間差:若聲源位於聽者右側,則右耳比左耳早聽到一毫秒.
  4. 兩耳朝向:若聲源在前邊則比後邊響
  5. 聲音最大距離:大於此距離聲音不再衰減.默認為DS3D_DEFAULTMAXDISTANCE(109 m)
  6. 聲音最小距離:大於此距離聲音不再遞增.默認為DS3D_DEFAULTMINDISTANCE(1m)
  7. 默認模式: DS3DMODE_NORMAL使用3D座標設定音源位置
  8. 頭部模式: DS3DMODE_HEADRELATIVE.可以設定音源位置,但監聽位置總為(0,0,0)
  9. 禁用3D模式:DS3DMODE_DISABLE 禁用3D聲音

WAV數據載入』後交與DirectSound代碼如下:

1.創建聲卡接口,NULL為與默認聲卡鏈接

IDirectSound8 * direct_sound3D = NULL;// 默認聲卡接口

DirectSoundCreate8(NULL,&direct_sound3D,NULL);

2.設置為優先等級,允許設定主緩存音頻數據格式並獲取IDirectSound3DListener8接口

direct_sound3D->SetCooperativeLevel(hWnd, DSSCL_PRIORITY);// 優先級別

3.初始化Directsound主緩存,主緩存代表聲卡,並設置格式

DSBUFFERDESC buffer;// 緩存格式描述

ZeroMemory(&buffer, sizeof(DSBUFFERDESC));// 清零

buffer.dwSize = sizeof(DSBUFFERDESC);// 所占空間

buffer.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;//3D緩存並使用聲卡緩存

4.創建默認聲卡緩存接口

IDirectSoundBuffer * DirectSound3D_Buffer = NULL;

direct_sound3D->CreateSoundBuffer(&buffer, &DirectSound3D_Buffer, NULL);

5.獲取3D空間『聽者』

IDirectSound3DListener8  * DirectSound3D_Listener;// 3D聽者

DirectSound3D_Buffer->QueryInterface(IID_IDirectSound3DListener, (VOID**)&DirectSound3D_Listener);

6.設置緩存數據格式

WAVEFORMATEX format;//緩存格式描敘

memset(&format, 0, sizeof(WAVEFORMATEX));

format.cbSize = 0;//總是0.

format.wFormatTag = WAVE_FORMAT_PCM;  //脈衝編碼格式

format.nChannels = 1;   //(聲道數目)1為單聲道回放

format.nSamplesPerSec = 11025;//(每秒的樣本)總是這個速度

format.wBitsPerSample = 8;//聲音為8Bit

format.nBlockAlign = 1;//字節對齊,單聲道8bit占1byte.雙聲道16bit占4yte.

format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; //每秒播放字節數

DirectSound3D_Buffer->SetFormat(&format); //設置主緩存音頻格式

7.現在建立輔助聲音緩存

DSBUFFERDESC desc;// 緩存格式描敘

memset(&desc,0,sizeof(DSBUFFERDESC));

desc.dwSize = sizeof(DSBUFFERDESC);//dsbd的大小

desc.dwFlags = DSBCAPS_CTRL3D |// 請求3D BUFFER

DSBCAPS_CTRLVOLUME | // 緩存擁有音量控制功能

DSBCAPS_CTRLFREQUENCY | // 緩存擁有頻率控制功能

DSBCAPS_GLOBALFOCUS |

DSBCAPS_CTRLPOSITIONNOTIFY |  // 3D位置

DSBCAPS_GETCURRENTPOSITION2; // 播放位置

desc.dwBufferBytes = sound3D->size;//聲音數據的大小.

desc.guid3DAlgorithm = DS3DALG_DEFAULT;// 主緩沖區在 DSBCAPS_CTRL3D 模式下可以啟用這個選項

desc.lpwfxFormat = &format;// 緩存數據格式

8.創建輔助聲音緩衝器

direct_sound3D->CreateSoundBuffer(&desc, &sound3D->buffer, NULL);

9.獲取3D空間緩存

sound3D->buffer->QueryInterface(IID_IDirectSound3DBuffer, (VOID**)&sound3D->buffer3D);

10.把數據寫入聲音輔助緩存,輔助(二級)聲音緩存是自循環

UCHAR  audio_ptr_1 = NULL,audio_ptr_2 = NULL; //指向緩存第一與第二部分

DWORD   audio_len_1 = 0, audio_len_2 = 0;  //第一與第二緩存長度

11.鎖住空間

sound3D->buffer->Lock(0,//寫入指針指向位置

sound3D->size,//要鎖定空間大小.

(void **)&audio_ptr_1,//第一個部分開始地址.

&audio_len_1,//第一個部分長度

(void **)&audio_ptr_2,//第二個部分開始地址.

&audio_len_2,//第二個部分長度

DSBLOCK_FROMWRITECURS );

11.複製數據到聲音緩沖存儲器,拷貝到圓形緩沖存儲器的第一段中

memcpy(audio_ptr_1, sound3D->data, audio_len_1);

12.拷貝到圓形緩沖存儲器的第二段中

memcpy(audio_ptr_2, (sound3D->data + audio_len_1), audio_len_2);

13.解鎖

sound3D->buffer->Unlock(audio_ptr_1, audio_len_1,audio_ptr_2, audio_len_2);

14.設置音源3D位置與距離

DS3DBUFFER  param;

memset(&param, 0, sizeof(DS3DBUFFER));

param.dwSize = sizeof(DS3DBUFFER);

sound3D->buffer3D->GetAllParameters(&param);

param.flMaxDistance = sound3D->max;// 聲音最大距離 DS3D_DEFAULTMAXDISTANCE

param.flMinDistance = sound3D->min;// 聲音最小距離 DS3D_DEFAULTMINDISTANCE

param.vPosition.x = x; //音源3D位置

param.vPosition.y = y;

param.vPosition.z = -z;// Z軸反轉

param.dwMode = DS3DMODE_NORMAL;// 默認,使用3D座標設定音源位置和

sound3D->buffer3D->SetAllParameters(&param, DS3D_IMMEDIATE);// 立即設定

15.正確設置音源因子與3D聽者位置,關鍵部分是不要設置DS3DMODE_HEADRELATIVE(頭部模式)

DS3DLISTENER  param;

memset(&param, 0, sizeof(DS3DLISTENER));

param.dwSize = sizeof(DS3DLISTENER);

DirectSound3D_Listener->GetAllParameters(&param);

param.flDopplerFactor = 0; //多譜勒頻移,一般設為0

param.flRolloffFactor = 0.1f; // 衰減率,此直越大衰減越快.一般設為0.1f~0.5f

param.vPosition.x = x; //聽者3D位置

param.vPosition.y = y;

param.vPosition.z = -z;

DirectSound3D_Listener->SetAllParameters(&param, DS3D_IMMEDIATE);

16.循環播放音頻數據

sound3D->buffer->Play(0,0,DSBPLAY_LOOPING);

17.單次播放

sound3D->buffer->Play(0, 0, 0);

18.停止音頻播放

sound3D->buffer->Stop();

19.釋放DirectSound3D

sound3D->buffer3D->Release();// 釋放3D緩存

sound3D->buffer->Release();// 釋放輔助緩存

DirectSound3D_Buffer->Release(); // 釋放聲卡主緩存

direct_sound3D->Release();// 釋放聲卡

 

DirectMusic之播放MIDI

DirectMusic之播放MIDI

DirectMusic主要用於播放midi數據,而且你無需寫分析器.DirectMusic自動完成所有操作,本.演示程式按ALT鍵彈出MENU再Open打開midi音檔.按+/-鍵控制音量.本想編譯X64版本卻出現『REGDB_E_CLASSNOTREG Class not registered.』所以只有x86程式下載:

DirectMusic是建基於DirectSound,不過DirectMusic會在Init()中自啟動它.DirectMusic是Dirext中第一個完全COM化組件,所以DirectMusic無需LIB庫.只需以下幾個頭文檔.

#include “..\DirectX\Include\dmplugin.h”

#include “..\DirectX\Include\dmksctrl.h”

#include “..\DirectX\Include\dmusici.h”

#include “..\DirectX\Include\dmusicc.h”

#include “..\DirectX\Include\dmusicf.h”

但安裝最新DXSDK安裝包(DX11),以上幾文件頭文件據然穩唔倒.我只可在舊版SDK拷貝過來工程文檔下.如果你把文檔拷貝到工程檔案下還需要小小修改#include <dmplugin.h>改為#include “dmplugin.h”

而編譯時DirectMusic有大量GUID無法鏈接.出現LNK2001下錯誤.

“錯誤  LNK2001        無法解析的外部符號 _CLSID_DirectMusicPerformance”

古計是從”dxguid.lib”刪除左.無需執著這些小問題.只要自已加上即可.你需要初此化GUID宏指令

#define INIT_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8)   const GUID name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}

並把下面GUID複製到全域變量

INIT_GUID(CLSID_DirectMusicPerformance, 0xd2ac2881, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicSegment,     0xd2ac2882, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicSegmentState,0xd2ac2883, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicGraph,       0xd2ac2884, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicStyle,       0xd2ac288a, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicChordMap,    0xd2ac288f, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicComposer,    0xd2ac2890, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicLoader,      0xd2ac2892, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicBand,        0x79ba9e00, 0xb6ee, 0x11d1, 0x86, 0xbe, 0x0, 0xc0, 0x4f, 0xbf, 0x8f, 0xef);

INIT_GUID(GUID_Download,            0xd2ac28a7,0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(GUID_Unload,              0xd2ac28a8,  0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(GUID_StandardMIDIFile,    0x6621075, 0xe92e, 0x11d1, 0xa8, 0xc5, 0x0, 0xc0, 0x4f, 0xa3, 0x72, 0x6e);

INIT_GUID(GUID_DirectMusicAllTypes, 0xd2ac2893, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(GUID_PerfMasterVolume,    0xd2ac28b1, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicLoader, 0x2ffaaca2, 0x5dca, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);

INIT_GUID(IID_IDirectMusicGetLoader, 0x68a04844, 0xd13d, 0x11d1, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);

INIT_GUID(IID_IDirectMusicObject, 0xd2ac28b5, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicSegment, 0xf96029a2, 0x4282, 0x11d2, 0x87, 0x17, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicSegmentState, 0xa3afdcc7, 0xd3ee, 0x11d1, 0xbc, 0x8d, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xeb);

INIT_GUID(IID_IDirectMusicPerformance, 0x7d43d03, 0x6523, 0x11d2, 0x87, 0x1d, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicGraph, 0x2befc277, 0x5497, 0x11d2, 0xbc, 0xcb, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xeb);

INIT_GUID(IID_IDirectMusicStyle, 0xd2ac28bd, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicChordMap, 0xd2ac28be, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicComposer, 0xd2ac28bf, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicBand, 0xd2ac28c0, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicLoader8, 0x19e7c08c, 0xa44, 0x4e6a, 0xa1, 0x16, 0x59, 0x5a, 0x7c, 0xd5, 0xde, 0x8c);

INIT_GUID(IID_IDirectMusicPerformance8, 0x679c4137, 0xc62e, 0x4147, 0xb2, 0xb4, 0x9d, 0x56, 0x9a, 0xcb, 0x25, 0x4c);

INIT_GUID(IID_IDirectMusicSegment8, 0xc6784488, 0x41a3, 0x418f, 0xaa, 0x15, 0xb3, 0x50, 0x93, 0xba, 0x42, 0xd4);

INIT_GUID(IID_IDirectMusicSegmentState8, 0xa50e4730, 0xae4, 0x48a7, 0x98, 0x39, 0xbc, 0x4, 0xbf, 0xe0, 0x77, 0x72);

INIT_GUID(IID_IDirectMusicStyle8, 0xfd24ad8a, 0xa260, 0x453d, 0xbf, 0x50, 0x6f, 0x93, 0x84, 0xf7, 0x9, 0x85);

接口對像 簡介
IDirectMusic 你無需自已創建DirectMusic,因為她會自動創建
IDirectMusicPerformance 對MIDI播放進行控制,構建該對象時同時創建IDirectMusic
IDirectMusicLoader 用於加載音頻文檔,如MIDI.這樣你以擁有MIDI的載入器
IDirectMusicSegment Segment代表音樂數據塊
IDirectMusicSegmentState 數據塊狀態
IDirectMusicProt MIDI音樂輸出硬件設備.微軟合成器
IDirectMusic 擁有一個DSP(Digital Signal Processing)MIDI數字實時轉換器

下面是IDirectMusic播放MIDI代碼簡介:

1.首先你必須初此化COM

CoInitialize(NULL);

2.創建DirectMusicPerformance接口

IDirectMusicPerformance    *DirectMusic_Performance = NULL;

CoCreateInstance(CLSID_DirectMusicPerformance,NULL, CLSCTX_INPROC,             IID_IDirectMusicPerformance,(LPVOID*)&DirectMusic_Performance);

3.初此化 DirectMusicPerformance設置為audiopath,自動構建IDirectMusic和IDirectSound接口

DirectMusic_Performance->Init(NULL, NULL, hWnd);

4.增加數字輸出播放端口,使用微軟合成器作為默認設備

DirectMusic_Performance->AddPort(NULL);

5.創建MIDI載入器

IDirectMusicLoader  * DirectMusic_Loader = NULL;

CoCreateInstance(CLSID_DirectMusicLoader,NULL,CLSCTX_INPROC,                   IID_IDirectMusicLoader8,(LPVOID*)&DirectMusic_Loader);

6.你需要在初此化時設定音量

long volume = DMUS_VOLUME_MAX;

DirectMusic_Performance->SetGlobalParam(GUID_PerfMasterVolume, &volume, sizeof(long));

7.載入midi音檔. DirectMusic非常聰面.你只需給出『路徑』和『文檔名』即可完成MIDI載入

DMUS_OBJECTDESC objdesc;

memset(&objdesc, 0, sizeof(DMUS_OBJECTDESC));// 清零

objdesc.dwSize = sizeof(DMUS_OBJECTDESC);

objdesc.guidClass = CLSID_DirectMusicSegment;

objdesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;

8.Utf8轉換為UNICODE寬字符

MultiByteToWideChar(CP_UTF8, NULL, filename, (int)strlen(filename), objdesc.wszFileName, DMUS_MAX_FILENAME);//文檔名

MultiByteToWideChar(CP_UTF8, NULL, category, (int)strlen(category), objdesc.wszCategory, DMUS_MAX_CATEGORY);//路徑

9.設置當前搜索目錄

hResult = DirectMusic_Loader->SetSearchDirectory(GUID_DirectMusicAllTypes, objdesc.wszCategory, false);

10.如果MIDI音檔編譯進資源文檔(resource)或者自已讀取MIDI音檔數據

objdesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_MEMORY;

objdesc.pbMemData = data;//指向midi數據

objdesc.llMemLength = size;// 數據長度

11.獲取IDirectMusicSegment音色庫接口,並自動載入midi數據

IDirectMusicSegment      * segment;

DirectMusic_Loader->GetObject(&objdesc,IID_IDirectMusicSegment,(void**)&segment);

12.指定音色庫segment

segment->SetParam(GUID_StandardMIDIFile,-1,0,0,DirectMusic_Performance);

13.將音色庫segment載入到IDirectMusicPerformance

segment->SetParam(GUID_Download, -1, 0, 0, DirectMusic_Performance);

14.設置循環播放

segment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);

15.單次循環播放

midi->segment->SetRepeats(0);

16.終於可以播放MIDI音檔

IDirectMusicSegmentState * segstate;

DirectMusic_Performance->PlaySegment(segment,0,0, &segstate);

17.停止MIDI播放

DirectMusic_Performance->Stop(segment, NULL, 0, 0);

18.禦載DLS樂器段

segment->SetParam(GUID_Unload, -1, 0, 0, (void*)DirectMusic_Performance);

19.釋放指定MIDI音樂段.

segment->Release();

20.停止播放所有的MIDI音樂段

DirectMusic_Performance->Stop(NULL,NULL, 0, 0);

21.關閉IDirectMusicPerformance對像.

DirectMusic_Performance->CloseDown();

22.釋放IDirectMusicPerformance對像.

DirectMusic_Performance->Release();

23.釋放載入器.

DirectMusic_Loader->Release();

DirectSound之播放聲音

DirectSound之播放聲音

DirectSound顧明思義可以讓你控制聲卡播放聲音和音樂. DirectSound由許多模塊和接口組成,編譯時需要庫文檔DSOUND.LIB和頭文檔DSOUND.H

WAV音檔播放演示程式,按+/-鍵控制聲音音量.按Open載入WAV音檔:下載

1.啟動DirectSound

IDirectSound接口:DirectSound的主COM對象.它代表聲卡硬件.若你裝有多個聲卡,則每個DirectSound對象代表一個聲卡.創建主聲卡對象,輸入NULL與默認聲卡鏈接

LPDIRECTSOUND       direct_sound;

DirectSoundCreate(NULL,&direct_sound,NULL);

if (direct_sound == NULL)

return false;

2.設置默認協作等級

設定遊戲控制聲卡的等級.一般設定為DSSCL_NORMAL即可.當程式具有焦點時,即可播放聲音.但並不妨礙其它程式. DirectSound自動創建22kHz,立體聲,8bit主緩存(默認).hWnd為當前窗口句柄

direct_sound->SetCooperativeLevel(hWnd,DSSCL_NORMAL);

參數 簡介
DSSCL_NORMAL 默認等級
DSSCL_PRIORITY 優先等級.允許設定主緩存數據格式
DSSCL_EXCLUSIVE 當窗口在前臺時擁有優先等級
DSSCL_WRITEPRIMARY 完全控制主緩存

3.輔助(二級)音頻緩存

『輔助緩存』即代表你想播放的音頻.但聲卡上SRAM卻容量有限,而且我印象中只在ISA版CREATIVE(創新)聲卡有出現.『輔助緩存』分為『靜態緩存』與『動態緩存』.『靜態緩存』用於存儲經常播放短聲音.『動態緩存』用於存儲較長聲音.DirectSound不斷播放不斷把音頻數據寫入『輔助緩存』.使用稱為Circular Buffering(環形緩存)結構,一個指針寫入音頻數據,另一個指針讀取音頻數據.創建輔助聲音緩衝器

HRESULT IDirectSound::CreateSoundBuffer(

LPCDSBUFFERDESC lpcDSBufferDesc,

LPLPDIRECTSOUNDBUFFER lplpDirectSoundBuffer,

IUnknown FAR* pUnkOuter

);

4.設置緩存格式描敘,單聲道11kHz,8bit

AVEFORMATEX format;

format.cbSize = 0;//高級,總是0.

format.wFormatTag = WAVE_FORMAT_PCM;//PCM編碼

format.nChannels = 1;//(聲道數目)1為單聲道播放

format.nSamplesPerSec = 11025;// 樣本速度11kHz

//設置字節對齊.

//單聲道乘以1字節(8Bit), 它將是1字節字節對齊.

//立體聲它將是雙聲道8Bit, 它將是2字節字節對齊.

//如果它是立體聲系統和16Bit它將是4字節對齊.

format.nBlockAlign     = 1;

format.wBitsPerSample = 8;//聲言位數為8BIT

format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign;// 每秒字節

3.設定聲音緩沖存儲器描敘表

DSBUFFERDESC buffer;// 描述結構

memset(&buffer,0,sizeof(DSBUFFERDESC));// 清零

buffer.dwSize = sizeof(DSBUFFERDESC);// 結構所占空間

//設置DirectSoundBuffer的描述結構的標誌

buffer.dwFlags = DSBCAPS_CTRLPAN | // 緩存擁有總控制功能

DSBCAPS_CTRLVOLUME | // 緩存擁有音量控制功能

DSBCAPS_CTRLFREQUENCY; // 緩存擁有頻率控制功能

buffer.dwBufferBytes = sound_size;//聲音數據大小.

buffer.lpwfxFormat  = &format;//”WAV文件格式描述結構”

4.創建輔助(二級)音頻緩存

LPDIRECTSOUNDBUFFER sound_buffer;

direct_sound->CreateSoundBuffer(&buffer, &sound_buffer, NULL);

if (sound_buffer == NULL)

return false;

5.鎖住緩存

UCHAR *audio_ptr_1 = NULL, //指向緩存第一部分

UCHAR *audio_ptr_2 = NULL; //指向緩存第二部分

DWORD  audio_len_1 = 0,  //第一緩存長度

DWORD  audio_len_2 = 0;  //第二緩存長度

sound_buffer->Lock(0,//寫入指針指向位置

sound_size,//要鎖定大小,音頻數據長度.

(void **)&audio_ptr_1,//第一個部分開始地址.

&audio_len_1,//第一個部分長度

(void **)&audio_ptr_2,//第二個部分開始地址.

&audio_len_2,//第二個部分長度

DSBLOCK_FROMWRITECURSOR);// 緩存當前寫入點將被鎖住.

6.拷貝到環形緩存第一部分, sound_size為音頻數據

memcpy(audio_ptr_1, sound_data, audio_len_1);

7.拷貝到環形緩存第二段中

memcpy(audio_ptr_2, (sound_data + audio_len_1), audio_len_2);

8.解鎖

sound_buffer->Unlock(audio_ptr_1,audio_len_1,audio_ptr_2,audio_len_2);

9.循環播放聲音

sound_buffer->Play(0,0,DSBPLAY_LOOPING);

10.若單次播放聲音

sound_buffer->Play(0, 0, 0);

11.當退出遊戲時需要釋放緩存和聲卡

sound_buffer->Release();

direct_sound->Release();

小米MAX2

小米MAX2

台『小米2S』經常報空間不足,點刪APP都一樣.而且可部機跌過落地,影相經常花屏.是時侯要換個台手機.睇來睇去都是『小米MAX2』擁有『128GB固態硬盤』『4GB記憶體』先是1699紋.喂一不足時款式以落後.拿到手後想通過『一鍵換機』將電話簿『連絡人』移到『小米MAX2』點知按『我是新手機』WIFI馬上關閉.你需要重新『開啟WLAN』.後來通過『同步』『連絡人』,支持快充台機只是微微發熱.

 

DirectInput之查詢滑鼠

DirectInput之查詢滑鼠

在windows你可以通過WM_MOUSEMOVE被動(消息驅動)接受滑鼠當前位置.但若在遊戲中若想得到滑鼠『偏移量』和『按扭』(是否按下)DirectInput則可以幫助你你重新獲得滑鼠每次『偏移量』和『按扭』信息(主動讀取).然後讓你可以重新繪畫新『指標』. 而無需Windows自帶『指標』.若你有多個滑鼠.則所有『偏移量』合併到單一設備中.

滑鼠移動演示程式:下載

演示代碼.在Init()中調用

1.調用DirectInputCreate()生成IDirectInput接口

IDirectInput * direct_input;

DirectInputCreate(GetModuleHandle(NULL),DIRECTINPUT_VERSION, &direct_input,NULL);

2.獲取滑鼠GUID_SysMouse為全域主滑鼠設備ID碼

direct_input->CreateDevice(GUID_SysMouse, &direct_mouse, NULL);

3.設置滑鼠協作等級(前臺) 非獨占訪問,訪問滑鼠時不會干涉其它應用程序訪問該滑鼠.當應用程序在前臺和後臺時都能時用該滑鼠.

direct_mouse->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE |                                             DISCL_BACKGROUND);

4.設置數據格式. c_dfDIMouse為DirectInput通用滑鼠全域常量

direct_mouse->SetDataFormat(&c_dfDIMouse);

5.從DirectInput獲取滑鼠

direct_mouse->Acquire();

6.載入滑鼠紋理,並且背景透明

mouse->texture.IsAlpha = true;

Load_File_Texture(&mouse->texture, hInstance, filename);

7.綁定紋理

Bind_Image_Texture(&mouse->texture,GL_REPEAT, GL_REPEAT,GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,GL_REPLACE);

8.滑鼠尺寸

mouse_size = 20;

9.現在你可以從滑鼠中獲取輸入.在你Update()更新中獲取滑鼠狀態.若你失去設備鍵盤,需要重新從DirectInput獲取鍵盤.

DIMOUSESTATE  state;

while (direct_mouse->GetDeviceState(sizeof(DIMOUSESTATE), &state) == DIERR_INPUTLOST)//

{         // 已失去設備,重新從DirectInput獲取鍵盤

if (direct_mouse->Acquire()==S_OK)

return false;

}

10.定義鼠標結構

typedef struct DIMOUSE_TYP{

int x, y;// 滑鼠座標

int screen_width, screen_height;// 窗口寬和高

float size;// 鼠標尺寸

bool button_left, button_right, button_middle; // 滑鼠按鈕,若true則為按下

TEXTURE texture;// 指標紋理

}DIMOUSE, *DIMOUSE_PTR;

DIMOUSE mouse;

11.重新計算滑鼠位置

mouse->x = mouse->x + state.lX;

mouse->y = mouse->y + state.lY;

12.判斷是否超出窗口邊界

if (mouse->x < 0)

mouse->x = 0;

if (mouse->x >= screen_width)

mouse->x = screen_width-1;

if (mouse->y < 0)

mouse->y = 0;

if (mouse->y >= screen_height)

mouse->y = screen_height – 1;

13.提取滑鼠按鈕

mouse->button_left = state.rgbButtons[0] & 0x80; //滑鼠左鍵

mouse->button_right = state.rgbButtons[1] & 0x80; // 滑鼠右鍵

mouse->button_middle = state.rgbButtons[2] & 0x80;// 滑鼠中鍵

14.當你退出遊戲時.需要釋放滑鼠和DirectInput對像

direct_mouse->Release();

direct_input->Release();

 

DirectInput之查詢鍵盤

DirectInput之查詢鍵盤

DirectInput是DirectX COM組件之一. 它讓你無需理會『鍵盤』硬件驅動程式. DirectInput為輸入設備提供統一接口,當然輸入設備需已安裝硬件驅動.而且你需要安裝『DirectX SDK』並設定include和LIB搜索路徑,或者加入工程項目中.若你想讀取鍵盤狀態.用法與GetAsyncKeyState()使用基本相似.但它的KEY鍵並非是ASCII碼.若你有多個鍵盤.則所有的輸入合併到單一設備中.

演示程式按鍵盤在屏幕中顯示對應字母:下載

演示代碼.在Init()中調用當然你需檢測返回值HRESULT

1.調用DirectInputCreate()生成IDirectInput接口

IDirectInput * direct_input;

DirectInputCreate(GetModuleHandle(NULL),DIRECTINPUT_VERSION, &direct_input,NULL);

2.獲取鍵盤, GUID_SysKeyboard為全域主鍵盤設備ID碼

IDirectInputDevice * direct_keyboard;

direct_input->CreateDevice(GUID_SysKeyboard, &direct_keyboard, NULL);

3.設置鍵盤協作等級, 非獨占訪問,訪問該設備時不會干涉其它應用程序訪問該設備. 並且當應用程序在前臺和後臺時都能時用該設備.

direct_keyboard->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE |DISCL_BACKGROUND);

4.設置數據格式. c_dfDIKeyboard為DirectInput通用鍵盤全域常量

direct_keyboard->SetDataFormat(&c_dfDIKeyboard);

5.從DirectInput獲取鍵盤.

direct_keyboard->Acquire();

6.你現在以可以從鍵盤中獲取輸入.在你Update()更新中獲取鍵盤狀態.若你失去設備鍵盤,需要重新從DirectInput獲取鍵盤

while(direct_keyboard->GetDeviceState(sizeof(UCHAR) * 256, direct_keystate)== DIERR_INPUTLOST)

{

if(direct_keyboard->Acquire() != S_OK)

return false;

}

7.當你退出遊戲時.需要釋放鍵盤和DirectInput對像

direct_keyboard->Release();

direct_input->Release();

OpenGL之陰影

OpenGL陰影

『陰影』有很多種方法,而『投射陰影』技術已被大量的應用於遊戲,從光源位置透視投影3D物體,從而將陰影投射到平面. 而且視覺效果佳.算法思路如下:

  1. 啟用『模板緩存』,將陰影繪畫限制在相應的區域中
  2. 構造投影矩陣,此矩陣根據『光源位置』和被『投影平面』位置來設定,並且與模型視圖矩陣相乘.
  3. 禁用光照和紋理,並設置顏色為黑色.
  4. 禁用深度測試.
  5. 啟用透明混合
  6. 繪畫陰影

落雪陰影演示下載:

構造投影矩陣

void Build_Matrix_Shadow(MATRIX4X4_PTR destMat, VECTOR4D_PTR lightPos, PLANE3D_PTR plane)

{

float dot;// 點積

dot = plane->M[0] * lightPos->M[0] +

plane->M[1] * lightPos->M[1] +

plane->M[2] * lightPos->M[2] +

plane->M[3] * lightPos->M[3];

// 第一列

destMat->_M[0] = dot – lightPos->M[0] * plane->M[0];

destMat->_M[4] = 0.0f – lightPos->M[0] * plane->M[1];

destMat->_M[8] = 0.0f – lightPos->M[0] * plane->M[2];

destMat->_M[12] = 0.0f – lightPos->M[0] * plane->M[3];

// 第二列

destMat->_M[1] = 0.0f – lightPos->M[1] * plane->M[0];

destMat->_M[5] = dot – lightPos->M[1] * plane->M[1];

destMat->_M[9] = 0.0f – lightPos->M[1] * plane->M[2];

destMat->_M[13] = 0.0f – lightPos->M[1] * plane->M[3];

// 第三列

destMat->_M[2] = 0.0f – lightPos->M[2] * plane->M[0];

destMat->_M[6] = 0.0f – lightPos->M[2] * plane->M[1];

destMat->_M[10] = dot – lightPos->M[2] * plane->M[2];

destMat->_M[14] = 0.0f – lightPos->M[2] * plane->M[3];

// 第四列

destMat->_M[3] = 0.0f – lightPos->M[3] * plane->M[0];

destMat->_M[7] = 0.0f – lightPos->M[3] * plane->M[1];

destMat->_M[11] = 0.0f – lightPos->M[3] * plane->M[2];

destMat->_M[15] = dot – lightPos->M[3] * plane->M[3];

}

MATRIX4X4 shadow_matrix; // 投影矩陣

GLfloat   light_pos[4] = { 150.0, 150.0, 150.0, 1.0 };// 『光照位置』

PLANE3D plane  = { 0.0, 1.0, 0.0, 0.0 };// 『投影平面』

構造投影矩陣

Build_Matrix_Shadow(&shadow_matrix,(VECTOR4D_PTR)&Light_Pos,(PLANE3D_PTR) &plane);

渲染陰影示例代碼

// 禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// 禁用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為只讀

glDepthMask(GL_FALSE);

// 啟用模板測試

glEnable(GL_STENCIL_TEST);

// 設置模板比較程序

glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

// 設置模板ref操作,

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);//

// 繪畫地板,將模板環存中相應的地板像素設為1

Draw_Floor();

// 啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

//  啟用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為可讀寫

glDepthMask(GL_TRUE);

// 設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);

// 設置模板操作

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// 保持當前值

// 繪製”鏡像”

glPushMatrix();

// 反轉

glScalef(1.0f, -1.0f, 1.0f);

//結束鏡像繪畫

glPopMatrix();

// 繪畫地板

Draw_Floor();

// 以黑色繪製陰影,將其與相應表面進行混合,無光照不進行深度測試

glPushMatrix();

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_TEXTURE_2D);// 禁用2D紋理

glDisable(GL_LIGHTING);// 禁用光照

glDisable(GL_DEPTH_TEST);// 禁用深度測試

glEnable(GL_BLEND);// 啟用混合

// 確保不在任何一個光柵位置處進行重複繪製

glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);

// 黑色陰影

glColor4f(0.0f, 0.0f, 0.0f, blend);

// 以陰影進行投影

glMultMatrixf((float*)&shadow_matrix);// 投影矩陣

// 繪畫粒子暴風雪陰影

Draw_Snowstorm(&snowstorm, &Camera3D, &billboard);

glEnable(GL_TEXTURE_2D);// 禁用2D紋理

glEnable(GL_LIGHTING);// 禁用光照

glEnable(GL_DEPTH_TEST);// 禁用深度測試

glDisable(GL_BLEND);// 啟用混合

glPopAttrib();

glPopMatrix();

// 禁用模板測試

glDisable(GL_STENCIL_TEST);

// 繪畫粒子暴風雪

Draw_Snowstorm();

 

OpenGL之鏡像

OpenGL鏡像

『鏡像』並非是真實世界中由光粒子所產生.而式通過模擬『鏡像』技術.工作原理如下:

  1. 禁用顏色和深度緩存
  2. 啟用『模板緩存』後繪畫地板,對『模板緩存』進行寫操作
  3. 然後反轉『平面軸』渲染3D多邊形物體『鏡像』.
  4. 在3D空間中渲染平面
  5. 在平面的正面渲染3D多邊形物體

『鏡像』演示程式下載:

『鏡像』示例代碼:

// 禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// 禁用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為只讀

glDepthMask(GL_FALSE);

// 啟用模板測試

glEnable(GL_STENCIL_TEST);

// 設置模板比較程序

glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

// 設置模板ref操作,

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);//

// 繪畫地板,將模板環存中相應的地板像素設為1

Draw_Floor();

// 啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

//  啟用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為可讀寫

glDepthMask(GL_TRUE);

// 設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);

// 設置模板操作

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// 保持當前值

// 繪製”鏡像”

glPushMatrix();

// 反轉

glScalef(1.0f, -1.0f, 1.0f);

//繪畫正方體

Draw_Cube();

//結束鏡像繪畫

glPopMatrix();

// 繪畫地板

Draw_Floor();

// 禁用模板測試

glDisable(GL_STENCIL_TEST);

//繪畫正方體

Draw_Cube();

OpenGL之迷霧

OpenGL之迷霧

『迷霧』通過模糊遠端之物,而近端清晰.給3D世界帶來霧感.而且可以減小進場境中多邊形數量,從而提高渲染速度.演示程式按『+/-』鍵加減『迷霧』密度.下載『迷霧

OpenGL直接提供『迷霧』支持:

1.首先需要啟用『迷霧』

glEnable(GL_FOG);

2.將像素與『迷霧』顏色進行混合而實現,其混合因子的設定跟據其視點距離, 『迷霧密度』和『迷霧模式』,設定『迷霧』可通過以下函式:

void glFogf(GLenum pname,GLfloat param);

void glFogfv(GLenum pname,const GLfloat*params);

void glFogi(GLenum pname,GLint param);

void glFogiv(GLenum pname,const GLint*params);

Pname(參數名) Params(參數值)
GL_FOG_MODE 『迷霧』混合方程式,默認為GL_EXP

GL_LINEAR:factor=(end-z)/(end-start)

GL_EXP:factor=e(-density*depth)

GL_EXP2: factor=e(-density*depth)* (-density*depth)

GL_FOG_DENSITY 迷霧密度默認為1.0f
GL_FOG_START 迷霧離視點近端默認為0
GL_FOG_END 迷霧離視點遠端默認為1
GL_FOG_INDEX 迷霧顏色索引值默認為0
GL_FOG_COLOR 迷霧顏色默認為黑色(0,0,0,0).

啟用迷霧函是代碼

void Enable_Fog()

{

float fogColor[] = { 0.5, 0.5, 0.5, 1.0 };// 灰色

glEnable(GL_FOG);// 啟用霧

glFogfv(GL_FOG_COLOR, fogColor);// 霧色

glFogf(GL_FOG_MODE, GL_EXP2);// 顏色模式

glFogf(GL_FOG_START, 200);// 霧近端範圍

glFogf(GL_FOG_END, 1000);// 霧遠端範圍

glFogf(GL_FOG_DENSITY, 0.06f);// 密度

}

 

OpenGL之粒子系統

OpenGL之粒子系統

『粒子系統』由多個『粒子』,每個『粒子』都有其獨立『屬性』如『尺寸』『顏色』『速度』,每個『粒子』都可獨立運作.當大量『粒子』同時運作便可產生其特的3D效果.『飄雪』『流星』『爆炸』『飛機引擎火焰』.

『飄雪』演示程式在天空落雪,時用『廣告牌』技術另廣告牌總是對這鏡頭.下載『飄雪

粒子系統大約分為

1.初此化粒子系統

2.生成粒子

3.更新粒子系統

4.渲染粒子系統

基本『粒子』屬性 簡介
位置 3D空間中的座標
速度 基本上粒子都會移動,你需要方向向量再乘以時間而求得速度
尺寸 粒子的尺寸可能變化
重量 外力對粒子影響
顏色 為粒子設定渲染顏色
形態 常見『點』『直線』『紋理四邊形』
宿主 粒子擁有者
生命週期 粒子存活時間

定義粒子結構

typedef struct PARTICLE_TYP {

VECTOR3D pos;// 位置

VECTOR3D velocity;// 速度與方向

VECTOR3D acceleration;// 加速度

float energy;// 生命週期(秒)

float size;// 尺寸

float weight;// 重量

float color[4];// 顏色

}PARTICLE,*PARTICLE_PTR;

『粒子系統』屬性 簡介
粒子隊列 要管理的所有粒子
數量 粒子量
位置 活動範圍中心位置
範圍 粒子活動範圍
屬性 常見『點』『直線』『紋理四邊形』
紋理 把紋理存儲在『粒子系統』

Particle(粒子系統)

typedef struct PARTICLE_SYSTEM_TYP{

int          attr;// 屬性

PARTICLE_PTR array;// 數組

int          count;// 實制個數

VECTOR3D     origin;// 原點

VECTOR3D     velocity;// 速度與方向

VECTOR3D     velocity_variation;// 速度變量

float        energy;// 生命週期(秒)

//有效範圍

float        height;// 高

float        width; // 寬

float        depth; // 深

float        size;// 尺寸

TEXTURE_PTR  texture;// 紋理

}PARTICLE_SYSTEM, *PARTICLE_SYSTEM_PTR;

1.初此化粒子系統

bool Init_Particle(PARTICLE_SYSTEM_PTR system, int count,

VECTOR3D_PTR origin, VECTOR3D_PTR velocity, VECTOR3D_PTR variation,

float height, float width, float depth,float size,float energy,

TEXTURE_PTR texture)// 紋理

{

if (system == NULL)

return false;

memset(system,0,sizeof(PARTICLE_SYSTEM));// 清空

system->count = count; // 粒子數量

system->array = (PARTICLE_PTR)malloc(sizeof(PARTICLE)*count);// 分配空間

system->texture = texture;// 紋理

system->size    = size;// 尺寸

system->height  = height;// 高

system->width   = width;// 寬

system->depth   = depth;// 深

Copy_VECTOR3D(&system->origin, origin);// 原點

Copy_VECTOR3D(&system->velocity, velocity);//  速度與方向

Copy_VECTOR3D(&system->velocity_variation, variation);//  速度變量

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < count; ++i)

{

particle = &system->array[i];// 粒子

Buid_Particle(system, particle);

}

return true;

}

2.生成粒子

void Buid_Particle(PARTICLE_SYSTEM_PTR  system, PARTICLE_PTR  particle)

{

particle->energy = system->energy;// 生命週期(秒)

particle->size   = system->size;// 尺寸

// 粒子位置

particle->pos.x = system->origin.x + FRAND_RANGE1() * (system->width /2.0f);// x

particle->pos.y = system->origin.y + FRAND_RANGE1() * (system->height/2.0f);// y

particle->pos.z = system->origin.z + FRAND_RANGE1() * (system->depth /2.0f);// z

// 粒子速度

particle->velocity.x = system->velocity.x  ;

particle->velocity.y = system->velocity.y  ;

particle->velocity.z = system->velocity.z  ;

}

3.更新粒子系統

void Update_Particle(PARTICLE_SYSTEM_PTR  system,float time)

{

PARTICLE_PTR         particle;// 粒子

for (int i = 0; i < system->count; ++i)

{

particle = &system->array[i];// 粒子

VECTOR3D vector;

Scale_VECTOR3D(&vector, &particle->velocity, time);

Add_VECTOR3D(&particle->pos, &particle->pos, &vector);// 移動粒子

// 判斷有無超出返圍

particle->energy = particle->energy – time;// 計算生命週期

if (particle->energy < 0 && particle->pos.y < 0)// 生命週期

Buid_Particle(system, particle);

}

}

4.繪畫貝粒子系統(紋理)

bool Draw_Texture_Particle(PARTICLE_SYSTEM_PTR system,

CAMERA3D_PTR camera, BILLBOARD_PTR billboard)

{

glPushMatrix();// 壓入矩陣

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_LIGHTING);// 禁用光照

glDisable(GL_LIGHT0);// 禁用0號燈光

glEnable(GL_DEPTH_TEST);// 啟用深度測試

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glEnable(GL_ALPHA_TEST);// 啟用透面測試

glAlphaFunc(GL_GREATER, 0);// 透面色

PARTICLE_PTR         particle;// 粒子

for (int i = 0; i < system->count; ++i)

{

particle = &system->array[i];// 粒子

//檢查3D球體是否為於視錐體內

if (Compute_Sphere_In_Frustum(&camera->frustum, &particle->pos, particle->size) == true)

{   // 繪畫Billboard廣告牌

Draw_Billboard(billboard, &particle->pos, particle->size);

}

}

glPopAttrib();// 彈出屬性

glPopMatrix();// 彈出矩陣

return true;

}