02.16

신승빈·2023년 2월 16일
0

KGCA 수업

목록 보기
109/128

Instancing

다수의 Instance를 1번의 DP call(Draw Primitive Call)을 통해 1개의 Object를 이용해서 한번에 그리는 것
FPS 최적화

        { "POSITION",	0, DXGI_FORMAT_R32G32B32_FLOAT,		0, 0,	D3D11_INPUT_PER_VERTEX_DATA,	0 },
        { "COLOR",		0, DXGI_FORMAT_R32G32B32A32_FLOAT,	0, 24,	D3D11_INPUT_PER_VERTEX_DATA,	0 },
		{ "TEXCOORD",   0, DXGI_FORMAT_R32G32_FLOAT,		0, 40,	D3D11_INPUT_PER_VERTEX_DATA,	0 },
		
		{ "mTransform", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA,     1 },
        { "mTransform", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 16, D3D11_INPUT_PER_INSTANCE_DATA,    1 },
        { "mTransform", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 32, D3D11_INPUT_PER_INSTANCE_DATA,    1 },
		{ "mTransform", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 48, D3D11_INPUT_PER_INSTANCE_DATA,    1 },
		{ "POSCOLOR",	0, DXGI_FORMAT_R32G32B32A32_FLOAT,	1, 64,	D3D11_INPUT_PER_INSTANCE_DATA,	1 },
    };

Instance로 넘어가는 Data는 D3D11_INPUT_PER_INSTANCE_DATA 속성으로 설정
InstanceDataStepRate는 해당 Instance를 몇 번 그릴 것인가

	pContext->DrawIndexedInstanced(m_dxobj.m_iNumIndex, m_ParticleList.size(), 0, 0, 0);

Draw Call 시 Instance의 개수를 추가로 넘기면 됨

struct VS_INPUT
{
	float3 p : POSITION;
	float4 c : COLOR;
	float2 t : TEXCOORD;
	row_major matrix mTransform : mTransform;
	float4 pc		: POSCOLOR;
	uint InstanceId : SV_InstanceID;
};

이 때, GPU에서는 SV_InstanceID라는 값을 Instance마다 제공
만약 상수버퍼에 SRT에 대한 Matrix를 저장했다면 SV_InstnaceID를 통해 접근 가능

참고

Instancing을 통해 다수의 Instance를 그리는 것과 Constant Buffer에 다수의 Instance의 SRT정보를 넘겨서 그리는 것은 차이가 있음
Constant Buffer에 Instance의 SRT정보를 저장해서 Draw를 한다고 해도 Vertex들이 Topology Primitive로 연결될 때 서로 다른 Instace들이 묶이므로 Instance 단위로 그릴 수 없을 것임

GeometryShader로 폭죽 구현하기


Point Particle과 Geometry Shader를 이용할 경우 Stream-Output Stage를 통해 Buffer에 동적으로 Particle을 추가함으로써 폭죽과 같은 효과를 낼 수 있음

void GSLauncherHandler( VSParticleIn input, inout PointStream<VSParticleIn> ParticleOutputStream )
{
    if(input.fTimer <= 0)
    {
        float3 vRandom = normalize( RandomDir( input.Type ) );
        //time to emit a new SHELL
        VSParticleIn output;
        output.vPos = input.vPos + input.vVel*g_fElapsedTime;
        output.vVel = input.vVel + vRandom*8.0;
        output.fTimer = P_SHELLLIFE + vRandom.y*0.5;
        output.Type = PT_SHELL;
        ParticleOutputStream.Append( output );
        
        //reset our timer
        input.fTimer = g_fSecondsPerFirework + vRandom.x*0.4;
    }
    else
    {
        input.fTimer -= g_fElapsedTime;
    }
    
    //emit ourselves to keep us alive
    ParticleOutputStream.Append( input );
    
}
void GSShellHandler( VSParticleIn input, inout PointStream<VSParticleIn> ParticleOutputStream )
{
    if(input.fTimer <= 0)
    {
        VSParticleIn output;
        float3 vRandom = float3(0,0,0);
   
		//time to emit a series of new Ember1s  
        for(int i=0; i< g_fNumEmber1s; i++)
        {
            vRandom = normalize( RandomDir( input.Type + i ) );
            output.vPos = input.vPos + input.vVel*g_fElapsedTime;
            output.vVel = input.vVel + vRandom*15.0;
            output.fTimer = P_EMBER1LIFE;
            output.Type = PT_EMBER1;
            ParticleOutputStream.Append( output );
        }
        
        //find out how many Ember2s to emit
        for(i=0; i<abs(vRandom.x)*g_fMaxEmber2s; i++)
        {
            vRandom = normalize( RandomDir( input.Type + i ) );
            output.vPos = input.vPos + input.vVel*g_fElapsedTime;
            output.vVel = input.vVel + vRandom*15.0;
            output.fTimer = P_EMBER2LIFE + 0.4*vRandom.x;
            output.Type = PT_EMBER2;
            ParticleOutputStream.Append( output );
        }
        
    }
    else
    {
        GSGenericHandler( input, ParticleOutputStream );
    }
}

이 때, 재미있는건 위 함수는 마지막에 무조건 OutputStream.Append()를 수행하지만 아래 함수에서는 시간이 다 경과된다면 수행하지 않는 것을 볼 수 있는데 이를 통해 동적으로 Point Particle의 생성-삭제를 수행할 수 있음

Shader에서 Random값

Shader는 기본적으로 Random함수를 지원하지 않음

Texture1D g_txRandom  : register(t1);// 텍스처 저장 레지스터(1번)
float3 RandomDir(float fOffset)
{
    float tCoord = (g_fGlobalTime + fOffset) / 300.0;
    float4 vColor = g_txRandom.SampleLevel( g_samPoint, tCoord, 0 );    
    return vColor.xyz;
}

대신 Texture에 미리 Random Value를 저장해놓은 후 time값을 이용해 가져오는 방법도 존재

Light

조명은 크게 지역조명-전역조명, 직접조명-간접조명으로 분류
Graphics에서는 직접조명-간접조명을 주로 다룸
전역 조명 : Radiosity, Ray Trancing, Precomputed Radiance Transfer, Poton Mapping, Caustics 등 존재

광원의 종류

Directinal light

태양광, 방향 존재, 위치 존재 하지 않음

Point Light

전구, 방향 존재하지 않음(사방으로 퍼짐), 위치 존재

Spot Light

손전등, 방향 존재, 위치 존재

직접 조명

DirectionalLightColor=AmbientLight+DiffuseLight+SpecularLightDirectional Light Color = Ambient Light + Diffuse Light + Specular Light

Ambient Light

광원에서 출발한 빛이 다른 Object에 반사되어 자신 Object에게 들어오게 된 조명
Ambient=constantcolorAmbient = constant * color

Diffuse Light

Diffuse=Sum[VdLdN(L)Attenuation]Diffuse = Sum[V_d * L_d * N \cdot (-L) * Attenuation]
VdV_d : Material의 color. 즉 Object가 표현할 수 있는 색의 범위. 일반적으로는 그냥 흰색 사용
LdL_d : Light의 Color.
SumSum : 1개 이상의 광원들에 대한 연산의 합
N(L)N \cdot (-L) : 램버트 조명 공식

Lighting 단어

Final Color = Texture Color Material Color Light Color
명도 : 0~1사이의 흑백 표현
채도 : 색상

Specular Light

반사 재질의 Object에 사용
거울에 반사된 빛을 바라본다면 빛의 영향을 받겠지만, 거울과 무관한 방향을 바라본다면 빛의 영향을 받지 않는다


Reflection=2(NormalLight)NormalLightReflection = 2 * (Normal \cdot Light) * Normal - Light

HalfVector

광원의 방향, Vertex Normal, 시선의 방향을 모두 계산하기보다는 간단하게 광원과 Vertex, 시선과 Vertex 두 Vector의 평균을 Normal과 내적하여 계산량을 줄이는 방법

Specular 계산 공식

Specular=VsLsSum[Ls(NH)nAttenuation]Specular = V_s * L_s * Sum[L_s * (N \cdot H)^n * Attenuation]
Specular=VsLsSum[Ls(VR)nAttenuation]Specular = V_s * L_s * Sum[L_s * (V \cdot R)^n * Attenuation]
SumSum : 모든 Specular 조명의 합
nn : 반사지수. 0~1사이 값으로 최대 조명치의 값은 건들지 않지만 만약 최대 조명보다 약한 조명이라면 n = 1보다 더 낮은 값을 반환하므로 조명이 급격하게 어두워지는 연산을 내며, 이를 통해 HighLight 효과를 낼 수 있음

Shader에서는 reflect(a, b)라는 함수 지원

smoothstep(min, max, x)

x < min이면 0, x > max 이면 1, 그 사이면 0~1사이의 값을 반환하는 Shader 함수
거리에 따른 빛의 감쇠, Spot Light의 각도 등에서 사용 가능

그 외

Cook-Torrence

Fresnel, Geometry Attenuation, Roughness를 이용한 계산 식. 대표적인 반사 공식

Fresnel

물의 반사 계수를 계산하는 공식
호수는 보는 각도에 따라 멀리서보면 하늘이, 가까이에서 보면 물 속이 보이게 된다

기하감쇠



Unblocking, Masking, Shadowing 등 기하적인 요인으로 인해 빛의 세기가 달라지는 현상

Rim Light

피사체의 뒤에서 강한 조명을 주어 피사체의 경계를 뚜렷하게 보여주는 것
빨간 배경에 붉은 피사체를 둘 경우 Rim Light가 없다면 그 경계가 잘 구분가지 않을 것이다

profile
이상을 길잡이 삼아 로망을 추구합니다.

0개의 댓글