효과 사용(Direct3D 9)
이 페이지에서는 효과를 생성하고 사용하는 방법을 보여줍니다. 다루는 topics 다음과 같은 방법을 포함합니다.
- 효과 만들기
- 효과 렌더링
- 의미 체계를 사용하여 효과 매개 변수 찾기
- 핸들을 사용하여 매개 변수를 효율적으로 가져오기 및 설정
- 주석을 사용하여 매개 변수 정보 추가
- 효과 매개 변수 공유
- 오프라인 효과 컴파일
- Preshaders를 사용하여 성능 향상
- 매개 변수 블록을 사용하여 효과 매개 변수 관리
효과 만들기
다음은 BasicHLSL 샘플에서 가져온 효과를 만드는 예제입니다. 디버그 셰이더를 만들기 위한 효과 만들기 코드는 OnCreateDevice에서 가져옵니다.
ID3DXEffect* g_pEffect = NULL;
DWORD dwShaderFlags = 0;
dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
dwShaderFlags |= D3DXSHADER_NO_PRESHADER;
// Read the D3DX effect file
WCHAR str[MAX_PATH];
DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"BasicHLSL.fx" );
D3DXCreateEffectFromFile(
pd3dDevice,
str,
NULL, // CONST D3DXMACRO* pDefines,
NULL, // LPD3DXINCLUDE pInclude,
dwShaderFlags,
NULL, // LPD3DXEFFECTPOOL pPool,
&g_pEffect,
NULL );
이 함수는 다음 인수를 사용합니다.
- 디바이스입니다.
- 효과 파일의 파일 이름입니다.
- 셰이더를 구문 분석하는 동안 사용할 NULL로 끝나는 #defines 목록에 대한 포인터입니다.
- 사용자가 작성한 포함 처리기에 대한 선택적 포인터입니다. 처리기는 #include resolve 필요할 때마다 프로세서에 의해 호출됩니다.
- 셰이더를 사용하는 방법에 대한 컴파일러 힌트를 제공하는 셰이더 컴파일 플래그입니다. 옵션에는 다음이 포함됩니다.
- 알려진 양수 셰이더가 컴파일되고 있는 경우 유효성 검사를 건너뜁합니다.
- 최적화를 건너뛰면(최적화로 디버깅이 더 어려워질 때도 사용됨)
- 디버그할 수 있도록 셰이더에 포함할 디버그 정보를 요청합니다.
- 효과 풀입니다. 둘 이상의 효과가 동일한 메모리 풀 포인터를 사용하는 경우 효과의 전역 변수가 서로 공유됩니다. 효과 변수를 공유할 필요가 없는 경우 메모리 풀을 NULL로 설정할 수 있습니다.
- 새 효과에 대한 포인터입니다.
- 유효성 검사 오류를 보낼 수 있는 버퍼에 대한 포인터입니다. 이 예제에서 매개 변수는 NULL 로 설정되었으며 사용되지 않았습니다.
참고
2006년 12월 SDK부터 DirectX 10 HLSL 컴파일러는 이제 DirectX 9 및 DirectX 10 모두에서 기본 컴파일러입니다. 자세한 내용은 효과 컴파일러 도구를 참조하세요.
효과 렌더링
디바이스에 효과 상태를 적용하기 위한 호출 시퀀스는 다음과 같습니다.
- ID3DXEffect::Begin 은 활성 기술을 설정합니다.
- ID3DXEffect::BeginPass 는 활성 패스를 설정합니다.
- ID3DXEffect::CommitChanges는 패스의 모든 집합 호출에 대한 변경 내용을 업데이트합니다. 그리기 호출 전에 호출해야 합니다.
- ID3DXEffect::EndPass 는 패스를 종료합니다.
- ID3DXEffect::End 는 활성 기술을 종료합니다.
효과 렌더링 코드는 효과가 없는 해당 렌더링 코드보다 더 간단합니다. 효과가 있는 렌더링 코드는 다음과 같습니다.
// Apply the technique contained in the effect
g_pEffect->Begin(&cPasses, 0);
for (iPass = 0; iPass < cPasses; iPass++)
{
g_pEffect->BeginPass(iPass);
// Only call CommitChanges if any state changes have happened
// after BeginPass is called
g_pEffect->CommitChanges();
// Render the mesh with the applied technique
g_pMesh->DrawSubset(0);
g_pEffect->EndPass();
}
g_pEffect->End();
렌더링 루프는 효과를 쿼리하여 포함된 패스 수를 확인하고 기술에 대한 모든 패스를 호출하는 것으로 구성됩니다. 렌더링 루프를 확장하여 각각 여러 패스가 있는 여러 기술을 호출할 수 있습니다.
의미 체계를 사용하여 효과 매개 변수 찾기
의미 체계는 애플리케이션이 매개 변수를 검색할 수 있도록 효과 매개 변수에 연결된 식별자입니다. 매개 변수에는 최대 하나의 의미 체계가 있을 수 있습니다. 의미 체계는 콜론 다음에 있습니다(:) 매개 변수 이름 뒤의 입니다. 예를 들면 다음과 같습니다.
float4x4 matWorldViewProj : WORLDVIEWPROJ;
의미 체계를 사용하지 않고 효과 전역 변수를 선언한 경우 대신 다음과 같이 표시됩니다.
float4x4 matWorldViewProj;
효과 인터페이스는 의미 체계를 사용하여 특정 효과 매개 변수에 대한 핸들을 가져올 수 있습니다. instance 경우 다음에서는 행렬의 핸들을 반환합니다.
D3DHANDLE handle =
m_pEffect->GetParameterBySemantic(NULL, "WORLDVIEWPROJ");
의미 체계 이름으로 검색하는 것 외에도 효과 인터페이스에는 매개 변수를 검색하는 다른 많은 메서드가 있습니다.
핸들을 사용하여 매개 변수를 효율적으로 가져오기 및 설정
핸들은 효과 매개 변수, 기술, 패스 및 주석을 효과와 함께 참조하는 효율적인 수단을 제공합니다. 핸들(D3DXHANDLE 형식)은 문자열 포인터입니다. GetParameterxxx 또는 GetAnnotationxxx와 같은 함수에 전달되는 핸들은 다음 세 가지 형식 중 하나일 수 있습니다.
- GetParameterxxx와 같은 함수에서 반환되는 핸들입니다.
- 매개 변수, 기술, 전달 또는 주석의 이름을 포함하는 문자열입니다.
- NULL로 설정된 핸들입니다.
이 예제에서는 WORLDVIEWPROJ 의미 체계가 연결된 매개 변수에 대한 핸들을 반환합니다.
D3DHANDLE handle =
m_pEffect->GetParameterBySemantic(NULL, "WORLDVIEWPROJ");
주석을 사용하여 매개 변수 정보 추가
주석은 모든 기술, 전달 또는 매개 변수에 연결할 수 있는 사용자별 데이터입니다. 주석은 개별 매개 변수에 정보를 추가하는 유연한 방법입니다. 정보를 다시 읽고 애플리케이션에서 선택하는 방식으로 사용할 수 있습니다. 주석은 모든 데이터 형식일 수 있으며 동적으로 추가할 수 있습니다. 주석 선언은 꺾쇠 괄호로 구분됩니다. 주석에는 다음이 포함됩니다.
- 데이터 형식입니다.
- 변수 이름입니다.
- 등호(=)입니다.
- 데이터 값입니다.
- 끝 세미콜론(;).
instance 경우 이 문서의 이전 예제 모두에 다음 주석이 포함되어 있습니다.
texture Tex0 < string name = "tiger.bmp"; >;
주석은 텍스처 개체에 연결되고 텍스처 개체를 초기화하는 데 사용해야 하는 텍스처 파일을 지정합니다. 주석은 텍스처 개체를 초기화하지 않으며 단순히 변수에 연결된 사용자 정보 조각입니다. 애플리케이션은 ID3DXBaseEffect::GetAnnotation 또는 ID3DXBaseEffect::GetAnnotationByName 을 사용하여 주석을 읽고 문자열을 반환할 수 있습니다. 애플리케이션에서 주석을 추가할 수도 있습니다.
각 주석:
- 숫자 또는 문자열이어야 합니다.
- 항상 기본값으로 초기화해야 합니다.
- 기술 및 패스(Direct3D 9) 및 최상위 효과 매개 변수와 연결할 수 있습니다.
- ID3DXEffect 또는 ID3DXEffectCompiler를 사용하여 에 쓰고 읽을 수 있습니다.
- ID3DXEffect를 사용하여 추가할 수 있습니다.
- 효과 내에서 참조할 수 없습니다.
- 하위 의미 체계 또는 하위 주석을 사용할 수 없습니다.
효과 매개 변수 공유
효과 매개 변수는 모두 효과에 선언된 비정적 변수입니다. 여기에는 전역 변수 및 주석이 포함될 수 있습니다. "공유" 키워드(keyword) 매개 변수를 선언한 다음 효과 풀을 사용하여 효과를 만들어 효과 매개 변수를 서로 다른 효과 간에 공유할 수 있습니다.
효과 풀에는 공유 효과 매개 변수가 포함됩니다. 풀은 ID3DXEffectPool 인터페이스를 반환하는 D3DXCreateEffectPool 을 호출하여 만들어집니다. 효과를 만들 때 인터페이스를 D3DXCreateEffectxxx 함수에 대한 입력으로 제공할 수 있습니다. 매개 변수를 여러 효과 간에 공유하려면 매개 변수가 각 공유 효과에서 동일한 이름, 형식 및 의미 체계를 가져야 합니다.
ID3DXEffectPool* g_pEffectPool = NULL; // Effect pool for sharing parameters
D3DXCreateEffectPool( &g_pEffectPool );
매개 변수를 공유하는 효과는 동일한 디바이스를 사용해야 합니다. 이는 여러 디바이스에서 디바이스 종속 매개 변수(예: 셰이더 또는 텍스처)의 공유를 방지하기 위해 적용됩니다. 매개 변수는 공유 매개 변수를 포함하는 효과가 해제될 때마다 풀에서 삭제됩니다. 매개 변수를 공유할 필요가 없는 경우 효과를 만들 때 효과 풀에 NULL 을 제공합니다.
복제된 효과는 복제되는 효과와 동일한 효과 풀을 사용합니다. 효과를 복제하면 전역 변수, 기술, 패스 및 주석을 비롯한 효과의 정확한 복사본이 만들어집니다.
오프라인 효과 컴파일
D3DXCreateEffect를 사용하여 런타임에 효과를 컴파일하거나 명령줄 컴파일러 도구 fxc.exe 사용하여 오프라인으로 효과를 컴파일할 수 있습니다. CompiledEffect 샘플의 효과에는 꼭짓점 셰이더, 픽셀 셰이더 및 한 가지 기술이 포함됩니다.
// File: CompiledEffect.fx
// Global variables
float4 g_MaterialAmbientColor; // Material's ambient color
...
// Texture samplers
sampler RenderTargetSampler =
...
// Type: Vertex shader
VS_OUTPUT RenderSceneVS( float4 vPos : POSITION,
float3 vNormal : NORMAL,
float2 vTexCoord0 : TEXCOORD0 )
{
...
};
// Type: Pixel shader
PS_OUTPUT RenderScenePS( VS_OUTPUT In )
{
...
}
// Type: Technique
technique RenderScene
{
pass P0
{
ZENABLE = true;
VertexShader = compile vs_1_1 RenderSceneVS();
PixelShader = compile ps_1_1 RenderScenePS();
}
}
Effect-Compiler Tool을 사용하여 vs_1_1 대한 셰이더를 컴파일하면 다음 어셈블리 셰이더 지침이 생성되었습니다.
//
// Generated by Microsoft (R) D3DX9 Shader Compiler 4.09.02.1188
//
// fxc /T vs_1_1 /E RenderSceneVS /Fc CompiledEffect.txt CompiledEffect.fx
//
//
// Parameters:
//
// float4 g_LightAmbient;
// float4 g_LightDiffuse;
// float3 g_LightDir;
// float4 g_MaterialAmbientColor;
// float4 g_MaterialDiffuseColor;
// float g_fTime;
// float4x4 g_mWorld;
// float4x4 g_mWorldViewProjection;
//
//
// Registers:
//
// Name Reg Size
// ---------------------- ----- ----
// g_mWorldViewProjection c0 4
// g_mWorld c4 3
// g_MaterialAmbientColor c7 1
// g_MaterialDiffuseColor c8 1
// g_LightDir c9 1
// g_LightAmbient c10 1
// g_LightDiffuse c11 1
// g_fTime c12 1
//
//
// Default values:
//
// g_LightDir
// c9 = { 0.57735, 0.57735, 0.57735, 0 };
//
// g_LightAmbient
// c10 = { 1, 1, 1, 1 };
//
// g_LightDiffuse
// c11 = { 1, 1, 1, 1 };
//
vs_1_1
def c13, 0.159154937, 0.25, 6.28318548, -3.14159274
def c14, -2.52398507e-007, 2.47609005e-005, -0.00138883968, 0.0416666418
def c15, -0.5, 1, 0.5, 0
dcl_position v0
dcl_normal v1
dcl_texcoord v2
mov r0.w, c12.x
mad r0.w, r0.w, c13.x, c13.y
expp r3.y, r0.w
mov r0.w, r3.y
mad r0.w, r0.w, c13.z, c13.w
mul r0.w, r0.w, r0.w
mad r1.w, r0.w, c14.x, c14.y
mad r1.w, r0.w, r1.w, c14.z
mad r1.w, r0.w, r1.w, c14.w
mad r1.w, r0.w, r1.w, c15.x
mad r0.w, r0.w, r1.w, c15.y
mul r0.w, r0.w, v0.x
mul r0.x, r0.w, c15.z
dp3 r1.x, v1, c4
dp3 r1.y, v1, c5
dp3 r1.z, v1, c6
mov r0.yzw, c15.w
dp3 r2.x, r1, r1
add r0, r0, v0
rsq r1.w, r2.x
dp4 oPos.x, r0, c0
mul r1.xyz, r1, r1.w
dp4 oPos.y, r0, c1
dp3 r1.x, r1, c9
dp4 oPos.z, r0, c2
max r1.w, r1.x, c15.w
mov r1.xyz, c8
mul r1.xyz, r1, c11
mov r2.xyz, c7
mul r2.xyz, r2, c10
dp4 oPos.w, r0, c3
mad oD0.xyz, r1, r1.w, r2
mov oD0.w, c15.y
mov oT0.xy, v2
// approximately 34 instruction slots used
프리섀더를 사용하여 성능 향상
프리쉐이더는 상수 셰이더 식을 미리 계산하여 셰이더 효율성을 높이는 기술입니다. 효과 컴파일러는 셰이더가 실행되기 전에 셰이더 본문에서 셰이더 계산을 자동으로 꺼내 CPU에서 실행합니다. 따라서 프리섀더가 효과와만 작동합니다. instance 경우 이러한 두 식을 실행하기 전에 셰이더 외부에서 평가할 수 있습니다.
mul(World,mul(View, Projection));
sin(time)
이동할 수 있는 셰이더 계산은 균일한 매개 변수와 연결된 계산입니다. 즉, 각 꼭짓점 또는 픽셀에 대해 계산이 변경되지 않습니다. 효과를 사용하는 경우 효과 컴파일러는 자동으로 프리셰이더를 생성하고 실행합니다. 사용할 플래그가 없습니다. 사전 섀더는 셰이더당 명령 수를 줄이고 셰이더가 사용하는 상수 레지스터 수를 줄일 수도 있습니다.
효과 컴파일러는 CPU와 GPU라는 두 가지 유형의 프로세서에 대한 셰이더 코드를 컴파일하기 때문에 일종의 다중 프로세서 컴파일러로 간주합니다. 또한 효과 컴파일러는 GPU에서 CPU로 코드를 이동하여 셰이더 성능을 개선하도록 설계되었습니다. 이는 정적 식을 루프에서 끌어와 매우 유사합니다. 위치를 월드 공간에서 프로젝션 공간으로 변환하고 텍스처 좌표를 복사하는 셰이더는 HLSL에서 다음과 같습니다.
float4x4 g_mWorldViewProjection; // World * View * Projection matrix
float4x4 g_mWorldInverse; // Inverse World matrix
float3 g_LightDir; // Light direction in world space
float4 g_LightDiffuse; // Diffuse color of the light
struct VS_OUTPUT
{
float4 Position : POSITION; // vertex position
float2 TextureUV : TEXCOORD0; // vertex texture coords
float4 Diffuse : COLOR0; // vertex diffuse color
};
VS_OUTPUT RenderSceneVS( float4 vPos : POSITION,
float3 vNormal : NORMAL,
float2 vTexCoord0 : TEXCOORD0)
{
VS_OUTPUT Output;
// Transform the position from object space to projection space
Output.Position = mul(vPos, g_mWorldViewProjection);
// Transform the light from world space to object space
float3 vLightObjectSpace = normalize(mul(g_LightDir, (float3x3)g_mWorldInverse));
// N dot L lighting
Output.Diffuse = max(0,dot(vNormal, vLightObjectSpace));
// Copy the texture coordinate
Output.TextureUV = vTexCoord0;
return Output;
}
technique RenderVS
{
pass P0
{
VertexShader = compile vs_1_1 RenderSceneVS();
}
}
효과 컴파일러 도구를 사용하여 vs_1_1 대한 셰이더를 컴파일하면 다음 어셈블리 지침이 생성됩니다.
technique RenderVS
{
pass P0
{
vertexshader =
asm {
//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
// Parameters:
//
// float3 g_LightDir;
// float4x4 g_mWorldInverse;
// float4x4 g_mWorldViewProjection;
//
//
// Registers:
//
// Name Reg Size
// ---------------------- ----- ----
// g_mWorldViewProjection c0 4
// g_mWorldInverse c4 3
// g_LightDir c7 1
//
vs_1_1
def c8, 0, 0, 0, 0
dcl_position v0
dcl_normal v1
dcl_texcoord v2
mov r1.xyz, c7
dp3 r0.x, r1, c4
dp3 r0.y, r1, c5
dp3 r0.z, r1, c6
dp4 oPos.x, v0, c0
dp3 r1.x, r0, r0
dp4 oPos.y, v0, c1
rsq r0.w, r1.x
dp4 oPos.z, v0, c2
mul r0.xyz, r0, r0.w
dp4 oPos.w, v0, c3
dp3 r0.x, v1, r0
max oD0, r0.x, c8.x
mov oT0.xy, v2
// approximately 14 instruction slots used
};
//No embedded pixel shader
}
}
이렇게 하면 약 14개의 슬롯이 사용되며 9개의 상수 레지스터가 사용됩니다. 프리쉐이더를 사용하면 컴파일러는 정적 식을 셰이더에서 프리셰이더로 이동합니다. 동일한 셰이더는 다음과 같이 프리셰이더를 사용하여 표시됩니다.
technique RenderVS
{
pass P0
{
vertexshader =
asm {
//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
// Parameters:
//
// float3 g_LightDir;
// float4x4 g_mWorldInverse;
//
//
// Registers:
//
// Name Reg Size
// --------------- ----- ----
// g_mWorldInverse c0 3
// g_LightDir c3 1
//
preshader
dot r0.x, c3.xyz, c0.xyz
dot r0.y, c3.xyz, c1.xyz
dot r0.z, c3.xyz, c2.xyz
dot r1.w, r0.xyz, r0.xyz
rsq r0.w, r1.w
mul c4.xyz, r0.w, r0.xyz
// approximately 6 instructions used
//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
// Parameters:
//
// float4x4 g_mWorldViewProjection;
//
//
// Registers:
//
// Name Reg Size
// ---------------------- ----- ----
// g_mWorldViewProjection c0 4
//
vs_1_1
def c5, 0, 0, 0, 0
dcl_position v0
dcl_normal v1
dcl_texcoord v2
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3
dp3 r0.x, v1, c4
max oD0, r0.x, c5.x
mov oT0.xy, v2
// approximately 7 instruction slots used
};
//No embedded pixel shader
}
}
효과는 셰이더를 실행하기 직전에 프리셰이더를 실행합니다. 셰이더의 유형에 따라 각 꼭짓점 또는 픽셀에 대해 실행해야 하는 명령 수가 감소했기 때문에 셰이더 성능이 향상되는 동일한 기능입니다. 또한 고정 식이 프리쉐이더로 이동된 결과로 셰이더에서 사용하는 상수 레지스터는 더 적습니다. 즉, 이전에 필요한 상수 레지스터 수로 제한되었던 셰이더는 이제 상수 레지스터가 더 적기 때문에 컴파일될 수 있습니다. 프리섀더에서 5%와 20%의 성능 향상을 기대하는 것이 합리적입니다.
입력 상수는 프리쉐이더의 출력 상수와 다릅니다. 출력 c1은 입력 c1 레지스터와 동일하지 않습니다. 프리쉐이더의 상수 레지스터에 쓰는 것은 실제로 해당 셰이더의 입력(상수) 슬롯에 기록됩니다.
// BaseDelta c0 1
// Refinements c1 1
preshader
mul c1.x, c0.x, (-2)
add c0.x, c0.x, c0.x
cmp c5.x, c1.x, (1), (0)
위의 cmp 명령은 프리셰이더 c1 값을 읽고 mul 명령은 꼭짓점 셰이더에서 사용할 하드웨어 셰이더 레지스터에 씁니다.
매개 변수 블록을 사용하여 효과 매개 변수 관리
매개 변수 블록은 효과 상태 변경의 블록입니다. 매개 변수 블록은 나중에 단일 호출로 적용할 수 있도록 상태 변경 내용을 기록할 수 있습니다. ID3DXEffect::BeginParameterBlock을 호출하여 매개 변수 블록을 만듭니다.
m_pEffect->SetTechnique( "RenderScene" );
m_pEffect->BeginParameterBlock();
D3DXVECTOR4 v4( Diffuse.r, Diffuse.g, Diffuse.b, Diffuse.a );
m_pEffect->SetVector( "g_vDiffuse", &v4 );
m_pEffect->SetFloat( "g_fReflectivity", fReflectivity );
m_pEffect->SetFloat( "g_fAnimSpeed", fAnimSpeed );
m_pEffect->SetFloat( "g_fSizeMul", fSize );
m_hParameters = m_pEffect->EndParameterBlock();
매개 변수 블록은 API 호출에 의해 적용된 네 가지 변경 내용을 저장합니다. ID3DXEffect::BeginParameterBlock을 호출하면 상태 변경 내용이 기록되기 시작합니다. ID3DXEffect::EndParameterBlock 은 매개 변수 블록에 변경 내용 추가를 중지하고 핸들을 반환합니다. 핸들은 ID3DXEffect::ApplyParameterBlock을 호출할 때 사용됩니다.
EffectParam 샘플에서 매개 변수 블록은 렌더링 시퀀스에 적용됩니다.
CObj g_aObj[NUM_OBJS]; // Object instances
if( SUCCEEDED( pd3dDevice->BeginScene() ) )
{
// Set the shared parameters using the first mesh's effect.
// Render the mesh objects
for( int i = 0; i < NUM_OBJS; ++i )
{
ID3DXEffect *pEffect = g_aObj[i].m_pEffect;
// Apply the parameters
pEffect->ApplyParameterBlock( g_aObj[i].m_hParameters );
...
pEffect->Begin( &cPasses, 0 );
for( iPass = 0; iPass < cPasses; iPass++ )
{
...
}
pEffect->End();
}
...
pd3dDevice->EndScene();
}
매개 변수 블록은 ID3DXEffect::Begin 이 호출되기 직전에 네 가지 상태 변경 내용의 값을 모두 설정합니다. 매개 변수 블록은 단일 API 호출을 사용하여 여러 상태 변경을 설정하는 편리한 방법입니다.
관련 항목