本来打算用Shader Model 2.0,但是经过测试,Shader Model 2.0的功能太原始,连for循环都无法支持,最后只能改用Shader Model 3.0。由于使用了HLSL编译Shader Model 3.0,所以需要VS2002-2010和DirectX 9.0c SDK 2004-2010, 不能使用VC6和DirectX 9.0b SDK。
DX是DirectX的缩写,SM是Shader Model的缩写。
如果改写成DirectX 10/11版本的话,最低应该需要D3D_FEATURE_LEVEL_9_3和ps_4_0_level_9_3,同时还需要编写一个简易的vs_4_0_level_9_3,因为vertex shader和pixel shader在10/11中都不是可选的。
但是由于vs_4_0_level_9_3和ps_4_0_level_9_3不支持通用循环,需要把循环展开成多次条件判断,512的指令数很容易就用超了,对于较复杂的运算,最低建议使用D3D_FEATURE_LEVEL_10_0、vs_4_0和ps_4_0。
<code class="language-txt">vs_1_1 - 128指令
vs_2_0 - 256指令
vs_4_0_level_9_1 - 256指令
vs_2_x - 256指令
vs_4_0_level_9_3 - 256指令
vs_3_0 - 最少512指令,上限到D3DCAPS9.MaxVertexShader30InstructionSlots,支持通用循环
vs_4_0 - 无限制,支持通用循环
ps_2_0 - 32纹理指令+64算术指令
ps_4_0_level_9_1 - 32纹理指令+64算术指令
ps_2_x - 最少96指令,上限到D3DCAPS9.D3DPSHADERCAPS2_0.NumInstructionSlots
ps_4_0_level_9_3 - 512指令
vs_3_0 - 最少512指令,上限到D3DCAPS9.MaxPixelShader30InstructionSlots,支持通用循环
vs_4_0 - 无限制,支持通用循环
</code>
dxmandelbrot.zip
14.89MB
ZIP
35次下载
GPU部分:
<code class="language-c">// complexsets.txt
float4 ps_mandelbrot(float2 tex : TEXCOORD0) : COLOR0
{
float2 zvar = float2(0, 0);
float i;
for (i = 0; i < 16 && zvar.x * zvar.x + zvar.y * zvar.y <= 16 4; i++) { float2 zvar2="zvar;" zvar.x="zvar2.x" * zvar2.x - zvar2.y + tex.x; zvar.y="zvar2.x" tex.y; } return float4(i 16.0, 0.0f, 1.0f); float4 ps_julia(float2 tex : texcoord0) color0 zvar="tex;" float i; for (i="0;" i < && 0.4f; 0.3f; code></=></code>
CPU部分:
<code class="language-c">// main.c
#include <windows.h>
#include <tchar.h>
// DirectX 9.0c SDK or higher
#include <d3d9.h>
#include <d3dx9.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(linker, "/nodefaultlib:libcp")
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
LPDIRECT3DPIXELSHADER9 g_pPSmandelbrot = NULL;
LPDIRECT3DPIXELSHADER9 g_pPSjulia = NULL;
LPDIRECT3DPIXELSHADER9 g_pPS = NULL;
typedef struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw;
FLOAT u, v;
}CUSTOMVERTEX;
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX1)
HRESULT InitD3D(HWND hWnd)
{
D3DPRESENT_PARAMETERS d3dpp;
if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
return E_FAIL;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
if (FAILED(IDirect3D9_CreateDevice(g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice)))
{
return E_FAIL;
}
return S_OK;
}
HRESULT CompileAndCreatePS(LPCTSTR filename, LPSTR funcname, LPDIRECT3DPIXELSHADER9 *pps)
{
LPD3DXBUFFER psbuf = NULL, errmsg = NULL;
HRESULT hr = S_OK;
hr = D3DXCompileShaderFromFile(filename, NULL, NULL,
funcname, "ps_3_0", 0, &psbuf, &errmsg, NULL);
if (errmsg)
{
MessageBoxA(NULL, (char*)errmsg->lpVtbl->GetBufferPointer(errmsg), "err", MB_ICONERROR);
errmsg->lpVtbl->Release(errmsg);
return hr;
}
else if (FAILED(hr))
{
return hr;
}
hr = IDirect3DDevice9_CreatePixelShader(g_pd3dDevice,
(DWORD*)psbuf->lpVtbl->GetBufferPointer(psbuf), pps);
psbuf->lpVtbl->Release(psbuf);
return S_OK;
}
HRESULT InitRes()
{
CUSTOMVERTEX vertices[] =
{
{ 0.0f, 0.0f, 0.5f, 1.0f, -2.2f, -2.2f, },
{ 480.0f, 0.0f, 0.5f, 1.0f, 2.2f, -2.2f, },
{ 0.0f, 480.0f, 0.5f, 1.0f, -2.2f, 2.2f, },
{ 480.0f, 480.0f, 0.5f, 1.0f, 2.2f, 2.2f, },
};
HRESULT hr = S_OK;
VOID* pVertices;
hr = IDirect3DDevice9_CreateVertexBuffer(g_pd3dDevice, 4*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL);
if (FAILED(hr))
{
return hr;
}
hr = IDirect3DVertexBuffer9_Lock(g_pVB, 0, sizeof(vertices), (void**)&pVertices, 0);
if (FAILED(hr))
return hr;
memcpy(pVertices, vertices, sizeof(vertices));
IDirect3DVertexBuffer9_Unlock(g_pVB);
CompileAndCreatePS(_T("complexsets.txt"), "ps_mandelbrot", &g_pPSmandelbrot);
CompileAndCreatePS(_T("complexsets.txt"), "ps_julia", &g_pPSjulia);
g_pPS = g_pPSmandelbrot;
return S_OK;
}
VOID Cleanup()
{
if (g_pPSjulia)
IDirect3DPixelShader9_Release(g_pPSjulia);
if (g_pPSmandelbrot)
IDirect3DPixelShader9_Release(g_pPSmandelbrot);
if (g_pVB != NULL)
IDirect3DVertexBuffer9_Release(g_pVB);
if (g_pd3dDevice != NULL)
IDirect3DDevice9_Release(g_pd3dDevice);
if (g_pD3D != NULL)
IDirect3D9_Release(g_pD3D);
}
VOID Render()
{
IDirect3DDevice9_Clear(g_pd3dDevice, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0);
if (SUCCEEDED(IDirect3DDevice9_BeginScene(g_pd3dDevice)))
{
IDirect3DDevice9_SetStreamSource(g_pd3dDevice, 0, g_pVB, 0, sizeof(CUSTOMVERTEX));
IDirect3DDevice9_SetFVF(g_pd3dDevice, D3DFVF_CUSTOMVERTEX);
IDirect3DDevice9_SetPixelShader(g_pd3dDevice, g_pPS);
IDirect3DDevice9_DrawPrimitive(g_pd3dDevice, D3DPT_TRIANGLESTRIP, 0, 2);
IDirect3DDevice9_EndScene(g_pd3dDevice);
}
IDirect3DDevice9_Present(g_pd3dDevice, NULL, NULL, NULL, NULL);
}
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN:
g_pPS = g_pPSmandelbrot;
return 0;
case WM_RBUTTONDOWN:
g_pPS = g_pPSjulia;
return 0;
case WM_DESTROY:
Cleanup();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nShowCmd)
{
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
hInst, LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), NULL, NULL,
_T("MainWndProc"), wc.hIcon };
RECT rc = { 0, 0, 480, 480 };
MSG msg;
HWND hWnd;
if (!RegisterClassEx(&wc))
return 0;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
hWnd = CreateWindow(wc.lpszClassName, _T("Mandelbrot and Julia"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
rc.right - rc.left, rc.bottom - rc.top,
NULL, NULL, wc.hInstance, NULL);
if (SUCCEEDED(InitD3D(hWnd)))
{
if (SUCCEEDED(InitRes()))
{
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Render();
}
}
}
}
return 0;
}
</d3dx9.h></d3d9.h></tchar.h></windows.h></code>
更新:支持Visual C++ 6.0和DirectX 9.0b SDK
通过DirectX 9.0c SDK的fxc将上述HLSL代码编译为Shader Model 3.0 ASM代码,即可支持DirectX 9.0b SDK。
ps_mandelbrot.txt:
<code class="language-asm">// ps_mandelbrot.txt
ps_3_0
def c0, 0, 0, 4, 1
def c1, 0.0625, 0, 0, 0
defi i0, 16, 0, 0, 0
dcl_texcoord v0.xy
mov r0.y, c0.y
mov r0.z, c0.y
mov r0.w, c0.y
rep i0
mul r0.x, r0.z, r0.z
mad r0.x, r0.y, r0.y, r0.x
break_lt c0.z, r0.x
mul r0.x, r0.z, r0.z
mad r0.x, r0.y, r0.y, -r0.x
add r0.x, r0.x, v0.x
dp2add r0.z, r0.y, r0.z, v0.y
add r0.w, r0.w, c0.w
mov r0.y, r0.x
endrep
mul oC0.x, r0.w, c1.x
mov oC0.yzw, c0.xyyw
</code>
ps_julia.txt
<code class="language-asm">// ps_julia.txt
ps_3_0
def c0, 0, 0, 4, 0.400000006
def c1, 0.300000012, 1, 0.0625, 0
defi i0, 16, 0, 0, 0
dcl_texcoord v0.xy
mov r0.y, v0.x
mov r0.z, v0.y
mov r0.w, c0.y
rep i0
mul r0.x, r0.z, r0.z
mad r0.x, r0.y, r0.y, r0.x
break_lt c0.z, r0.x
mul r0.x, r0.z, r0.z
mad r0.x, r0.y, r0.y, -r0.x
add r0.x, r0.x, c0.w
dp2add r0.z, r0.y, r0.z, c1.x
add r0.w, r0.w, c1.y
mov r0.y, r0.x
endrep
mul oC0.x, r0.w, c1.z
mov oC0.yzw, c1.xwwy
</code>
main.c
<code class="language-c">// main.c
#include <windows.h>
#include <tchar.h>
#include <d3d9.h>
#include <d3dx9.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
LPDIRECT3DPIXELSHADER9 g_pPSmandelbrot = NULL;
LPDIRECT3DPIXELSHADER9 g_pPSjulia = NULL;
LPDIRECT3DPIXELSHADER9 g_pPS = NULL;
typedef struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw;
FLOAT u, v;
}CUSTOMVERTEX;
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX1)
HRESULT InitD3D(HWND hWnd)
{
D3DPRESENT_PARAMETERS d3dpp;
if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
return E_FAIL;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
if (FAILED(IDirect3D9_CreateDevice(g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice)))
{
return E_FAIL;
}
return S_OK;
}
HRESULT AssembleAndCreatePS(LPCTSTR filename, LPDIRECT3DPIXELSHADER9 *pps)
{
LPD3DXBUFFER psbuf = NULL, errmsg = NULL;
HRESULT hr = S_OK;
hr = D3DXAssembleShaderFromFile(filename, NULL, NULL, 0, &psbuf, &errmsg);
if (errmsg)
{
MessageBoxA(NULL, (char*)errmsg->lpVtbl->GetBufferPointer(errmsg), "err", MB_ICONERROR);
errmsg->lpVtbl->Release(errmsg);
return hr;
}
else if (FAILED(hr))
{
return hr;
}
hr = IDirect3DDevice9_CreatePixelShader(g_pd3dDevice,
(DWORD*)psbuf->lpVtbl->GetBufferPointer(psbuf), pps);
psbuf->lpVtbl->Release(psbuf);
return S_OK;
}
HRESULT InitRes()
{
CUSTOMVERTEX vertices[] =
{
{ 0.0f, 0.0f, 0.5f, 1.0f, -2.2f, -2.2f, },
{ 480.0f, 0.0f, 0.5f, 1.0f, 2.2f, -2.2f, },
{ 0.0f, 480.0f, 0.5f, 1.0f, -2.2f, 2.2f, },
{ 480.0f, 480.0f, 0.5f, 1.0f, 2.2f, 2.2f, },
};
HRESULT hr = S_OK;
VOID* pVertices;
hr = IDirect3DDevice9_CreateVertexBuffer(g_pd3dDevice, 4*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL);
if (FAILED(hr))
{
return hr;
}
hr = IDirect3DVertexBuffer9_Lock(g_pVB, 0, sizeof(vertices), (void**)&pVertices, 0);
if (FAILED(hr))
return hr;
memcpy(pVertices, vertices, sizeof(vertices));
IDirect3DVertexBuffer9_Unlock(g_pVB);
AssembleAndCreatePS(_T("ps_mandelbrot.txt"), &g_pPSmandelbrot);
AssembleAndCreatePS(_T("ps_julia.txt"), &g_pPSjulia);
g_pPS = g_pPSmandelbrot;
return S_OK;
}
VOID Cleanup()
{
if (g_pPSjulia)
IDirect3DPixelShader9_Release(g_pPSjulia);
if (g_pPSmandelbrot)
IDirect3DPixelShader9_Release(g_pPSmandelbrot);
if (g_pVB != NULL)
IDirect3DVertexBuffer9_Release(g_pVB);
if (g_pd3dDevice != NULL)
IDirect3DDevice9_Release(g_pd3dDevice);
if (g_pD3D != NULL)
IDirect3D9_Release(g_pD3D);
}
VOID Render()
{
IDirect3DDevice9_Clear(g_pd3dDevice, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0);
if (SUCCEEDED(IDirect3DDevice9_BeginScene(g_pd3dDevice)))
{
IDirect3DDevice9_SetStreamSource(g_pd3dDevice, 0, g_pVB, 0, sizeof(CUSTOMVERTEX));
IDirect3DDevice9_SetFVF(g_pd3dDevice, D3DFVF_CUSTOMVERTEX);
IDirect3DDevice9_SetPixelShader(g_pd3dDevice, g_pPS);
IDirect3DDevice9_DrawPrimitive(g_pd3dDevice, D3DPT_TRIANGLESTRIP, 0, 2);
IDirect3DDevice9_EndScene(g_pd3dDevice);
}
IDirect3DDevice9_Present(g_pd3dDevice, NULL, NULL, NULL, NULL);
}
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN:
g_pPS = g_pPSmandelbrot;
return 0;
case WM_RBUTTONDOWN:
g_pPS = g_pPSjulia;
return 0;
case WM_DESTROY:
Cleanup();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nShowCmd)
{
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
hInst, LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), NULL, NULL,
_T("MainWndProc"), wc.hIcon };
RECT rc = { 0, 0, 480, 480 };
MSG msg;
HWND hWnd;
if (!RegisterClassEx(&wc))
return 0;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
hWnd = CreateWindow(wc.lpszClassName, _T("Mandelbrot and Julia"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
rc.right - rc.left, rc.bottom - rc.top,
NULL, NULL, wc.hInstance, NULL);
if (SUCCEEDED(InitD3D(hWnd)))
{
if (SUCCEEDED(InitRes()))
{
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Render();
}
}
}
}
return 0;
}
</d3dx9.h></d3d9.h></tchar.h></windows.h></code>
200字以内,仅用于支线交流,主线讨论请采用回复功能。